Skip to content
On this page

API de plugin

Les plugins Vite étendent l'interface de plugin bien conçue de Rollup avec quelques options supplémentaires spécifiques à Vite. En conséquence, vous pouvez écrire un plugin Vite une fois et le faire fonctionner à la fois pour dev et build.

Il est recommandé de parcourir la [documentation des plugins de Rollup] (https://rollupjs.org/guide/en/#plugin-development) avant de lire les sections ci-dessous.

Création d'un plugin

Vite s'efforce d'offrir des modèles établis dès le départ, donc avant de créer un nouveau plugin, assurez-vous de vérifier le Guide des fonctionnalités pour voir si votre besoin est couvert. Examinez également les plugins communautaires disponibles, sous la forme d'un [plugin compatible Rollup] (https://github.com/rollup/awesome) et de [plugins spécifiques à Vite] (https://github.com/vitejs/awesome-vite#plugins).

Lorsque vous créez un plugin, vous pouvez le mettre en ligne dans votre vite.config.js. Il n'est pas nécessaire de créer un nouveau paquet pour lui. Lorsque vous voyez qu'un plugin a été utile dans vos projets, pensez à le partager pour aider les autres [dans l'écosystème] (https://chat.vitejs.dev).

TIP

Lorsque vous apprenez, déboguez ou créez des plugins, nous vous suggérons d'inclure vite-plugin-inspect dans votre projet. Il vous permet d'inspecter l'état intermédiaire des plugins Vite. Après l'installation, vous pouvez visiter localhost:5173/__inspect/ pour inspecter les modules et la pile de transformation de votre projet. Consultez les instructions d'installation dans la vite-plugin-inspect docs. !vite-plugin-inspect](/images/vite-plugin-inspect.png)

Conventions

Si le plugin n'utilise pas de hooks spécifiques à Vite et peut être implémenté comme un Plugin Rollup compatible, il est recommandé d'utiliser les conventions de nommage des plugins Rollup.

  • Les plugins Rollup doivent avoir un nom clair avec le préfixe rollup-plugin-.
  • Incluez les mots-clés rollup-plugin et vite-plugin dans le package.json.

Cela permet d'utiliser le plugin dans des projets purement Rollup ou WMR.

Pour les plugins Vite uniquement

  • Les plugins Vite doivent avoir un nom clair avec le préfixe vite-plugin-.
  • Inclure le mot-clé vite-plugin dans le package.json.
  • Inclure une section dans la documentation du plugin détaillant pourquoi il s'agit d'un plugin Vite uniquement (par exemple, il utilise des hooks de plugin spécifiques à Vite).

Si votre plugin ne fonctionne que pour un framework particulier, son nom doit être inclus dans le préfixe.

  • vite-plugin-vue- préfixe pour les plugins Vue
  • vite-plugin-react- préfixe pour les plugins React
  • vite-plugin-svelte- préfixe pour les plugins Svelte

Voir aussi Virtual Modules Convention.

Configuration des plugins

Les utilisateurs ajouteront des plugins au projet devDependencies et les configureront en utilisant l'option de tableau plugins.

js
// vite.config.js
import vitePlugin from 'vite-plugin-feature'.
import rollupPlugin from 'rollup-plugin-feature' (en anglais)

export default defineConfig({
  plugins : [vitePlugin(), rollupPlugin()]
})

Les plugins malheureux seront ignorés, ce qui peut être utilisé pour activer ou désactiver facilement les plugins.

plugins accepte également les préréglages incluant plusieurs plugins comme un seul élément. Ceci est utile pour les fonctionnalités complexes (comme l'intégration de frameworks) qui sont implémentées en utilisant plusieurs plugins. Le tableau sera aplati en interne.

js
// plugin de framework
import frameworkRefresh from 'vite-plugin-framework-refresh'.
import frameworkDevtools from 'vite-plugin-framework-devtools'.

export default function framework(config) {
  retourne [frameworkRefresh(config), frameworkDevTools(config)].
}
js
// vite.config.js
import {  defineConfig } from 'vite'.
import framework depuis 'vite-plugin-framework'.

export default defineConfig({
  plugins : [framework()]
})

Exemples simples

TIP

Il est courant de créer un plugin Vite/Rollup avec une fonction d'usine qui renvoie l'objet du plugin. La fonction peut accepter des options qui permettent aux utilisateurs de personnaliser le comportement du plugin.

Transformer les types de fichiers personnalisés

js
const fileRegex = /\.(mon-fichier-ext)$/

export default function myPlugin() {
  return {
    name : 'transform-file',

    transform(src, id) {
      if (fileRegex.test(id)) {
        return {
          code : compileFileToJS(src),
          map : null // fournit la carte de la source si elle est disponible
        }
      }
    }
  }
}

Importation d'un fichier virtuel

Voir l'exemple dans la [section suivante] (#virtual-modules-convention).

Convention des modules virtuels

Les modules virtuels sont un schéma utile qui vous permet de transmettre des informations de compilation aux fichiers sources en utilisant la syntaxe d'importation ESM normale.

js
export default function myPlugin() {
  const virtualModuleId = "virtual:my-module" (module virtuel)
  const resolvedVirtualModuleId = '\0' + virtualModuleId

  return {
    name : 'my-plugin', // obligatoire, apparaîtra dans les avertissements et les erreurs
    resolveId(id) {
      si (id === virtualModuleId) {
        return resolvedVirtualModuleId
      }
    },
    load(id) {
      si (id === resolvedVirtualModuleId) {
        return `export const msg = "from virtual module"`
      }
    }
  }
}

Ce qui permet d'importer le module en JavaScript :

js
import {  msg } from 'virtual:my-module'.

console.log(msg)

Les modules virtuels dans Vite (et Rollup) sont préfixés par convention avec virtual: pour le chemin d'accès de l'utilisateur. Si possible, le nom du plugin doit être utilisé comme un espace de nom pour éviter les collisions avec d'autres plugins dans l'écosystème. Par exemple, un vite-plugin-posts pourrait demander aux utilisateurs d'importer un module virtuel virtual:posts ou virtual:posts/helpers pour obtenir des informations sur le temps de construction. En interne, les plugins qui utilisent des modules virtuels doivent préfixer l'ID du module par \0 lors de la résolution de l'id, une convention de l'écosystème rollup. Cela empêche les autres plugins d'essayer de traiter l'identifiant (comme la résolution de nœuds), et les fonctionnalités principales comme les cartes de source peuvent utiliser cette information pour différencier les modules virtuels des fichiers ordinaires. \0 n'est pas un caractère autorisé dans les URL d'importation, nous devons donc les remplacer pendant l'analyse de l'importation. Un identifiant virtuel \0{id} finit par être encodé comme /@id/__x00__{id} pendant le développement dans le navigateur. L'identifiant sera décodé avant d'entrer dans le pipeline des plugins, il ne sera donc pas vu par le code des hooks des plugins.

Notez que les modules directement dérivés d'un fichier réel, comme dans le cas d'un module de script dans un Single File Component (comme un SFC .vue ou .svelte) n'ont pas besoin de suivre cette convention. Les SFCs génèrent généralement un ensemble de sous-modules lorsqu'ils sont traités, mais le code de ces derniers peut être mappé sur le système de fichiers. L'utilisation de ``0` pour ces sous-modules empêcherait le fonctionnement correct des cartes de source.

Crochets universels

Pendant le développement, le serveur de développement Vite crée un conteneur de plugin qui invoque Rollup Build Hooks de la même manière que Rollup.

Les hooks suivants sont appelés une fois au démarrage du serveur :

Les hooks suivants sont appelés à chaque requête de module entrante :

Les hooks suivants sont appelés lorsque le serveur est fermé :

Notez que le hook moduleParsed n'est pas appelé pendant le dev, car Vite évite les analyses AST complètes pour de meilleures performances.

Les Output Generation Hooks (sauf closeBundle) ne sont pas appelés pendant le développement. Vous pouvez considérer que le serveur de développement de Vite appelle seulement rollup.rollup() sans appeler bundle.generate().

Crochets spécifiques à Vite

Les plugins Vite peuvent aussi fournir des hooks qui servent des objectifs spécifiques à Vite. Ces hooks sont ignorés par Rollup.

config

  • Type: (config : UserConfig, env : { mode : string, command : string }) => UserConfig | null | void

  • Kind: async, sequential

    Modifie la configuration de Vite avant qu'elle ne soit résolue. Le hook reçoit la configuration utilisateur brute (options CLI fusionnées avec le fichier de configuration) et la config env courante qui expose le mode et la commande utilisés. Il peut retourner un objet de configuration partielle qui sera profondément fusionné dans la configuration existante, ou directement muter la configuration (si la fusion par défaut ne permet pas d'obtenir le résultat souhaité).

    Exemple:

    js
    // renvoie une configuration partielle (recommandé)
    const partialConfigPlugin = () => ({
      name : 'return-partial',
      config : () => ({
        résoudre : {
          alias : {
            foo : 'bar
          }
        }
      })
    })
    
    // Mutation directe de la configuration (à utiliser uniquement lorsque la fusion ne fonctionne pas)
    const mutateConfigPlugin = () => ({
      nom : 'mutate-config',
      config(config, { command }) {
        if (command === 'build') {
          config.root = 'foo'
        }
      }
    })
    

    Remarque

    Les plugins utilisateurs sont résolus avant l'exécution de ce hook, donc l'injection d'autres plugins dans le hook config n'aura aucun effet.

configResolved

  • Type: (config : ResolvedConfig) => void | Promise<void>

  • Kind: async, parallel

    Appelé après la résolution de la configuration Vite. Utilisez ce hook pour lire et stocker la configuration finale résolue. Il est également utile lorsque le plugin doit faire quelque chose de différent en fonction de la commande exécutée.

    Exemple:

    js
    const examplePlugin = () => {
      let config
    
      return {
        name : 'read-config',
    
        configResolved(resolvedConfig) {
          // stocke la configuration résolue
          config = resolvedConfig
        },
    
        // utilisation de la configuration stockée dans d'autres hooks
        transform(code, id) {
          if (config.command === 'serve') {
            // dev : plugin invoqué par le serveur de développement
          } else {
            // build : plugin invoqué par Rollup
          }
        }
      }
    }
    

    Notez que la valeur de command est serve dans dev (dans le cli vite, vite dev, et vite serve sont des alias).

configureServer

  • Type: (server : ViteDevServer) => (() => void) | void | Promise<(() => void) | void>

  • Kind: async, sequential

  • Voir aussi:** ViteDevServer

    Crochet pour configurer le serveur de développement. Le cas d'utilisation le plus courant est l'ajout d'intergiciels personnalisés à l'application interne connect :

    js
    const myPlugin = () => ({
      nom : 'configure-server',
      configureServer(server) {
        server.middlewares.use((req, res, next) => {
          // traitement personnalisé de la demande...
        })
      }
    })
    

    Injection d'un middleware pour les postes

    Le hook configureServer est appelé avant que les middlewares internes soient installés, donc les middlewares personnalisés seront exécutés avant les middlewares internes par défaut. Si vous voulez injecter un middleware après les middlewares internes, vous pouvez retourner une fonction de configureServer, qui sera appelée après l'installation des middlewares internes :

    js
    const myPlugin = () => ({
      nom : 'configure-server',
      configureServer(server) {
        // retourne un post hook qui est appelé après que les middlewares internes soient
        // installés
        return () => {
          server.middlewares.use((req, res, next) => {
            // traitement personnalisé de la demande...
          })
        }
      }
    })
    

    Stockage de l'accès au serveur

    Dans certains cas, d'autres hooks de plugins peuvent avoir besoin d'accéder à l'instance du serveur de développement (par exemple, accéder au serveur de socket web, à l'observateur de système de fichiers, ou au graphe de modules). Ce hook peut également être utilisé pour stocker l'instance du serveur afin que d'autres hooks puissent y accéder :

    js
    const myPlugin = () => {
      let server
      return {
        nom : 'configure-server',
        configureServer(_server) {
          server = _server
        },
        transform(code, id) {
          if (serveur) {
            // utilise le serveur...
          }
        }
      }
    }
    

    Notez que configureServer n'est pas appelé lors de l'exécution du build de production, donc vos autres hooks doivent se prémunir contre son absence.

configurePreviewServer

  • Type: (server : { middlewares : Connect.Server, httpServer : http.Server }) => (() => void) | void | Promise<(() => void) | void>

  • Kind: async, sequential

    Identique à configureServer mais pour le serveur de prévisualisation. Il fournit le serveur connect et son [serveur http] sous-jacent(https://nodejs.org/api/http.html). Comme pour configureServer, le hook configurePreviewServer est appelé avant que les autres middlewares ne soient installés. Si vous voulez injecter un middleware après d'autres middlewares, vous pouvez retourner une fonction de configurePreviewServer, qui sera appelée après l'installation des middlewares internes :

    js
    const myPlugin = () => ({
      nom : 'configure-preview-server',
      configurePreviewServer(server) {
        // retourne un post hook qui est appelé après que les autres middlewares soient
        // installés
        return () => {
          server.middlewares.use((req, res, next) => {
            // traitement personnalisé de la demande...
          })
        }
      }
    })
    

transformIndexHtml

  • Type: IndexHtmlTransformHook | { enforce? : 'pre' | 'post', transform : IndexHtmlTransformHook }

  • Kind: async, sequential

    Crochet dédié à la transformation des fichiers d'entrée HTML tels que index.html. Le hook reçoit la chaîne HTML courante et un contexte de transformation. Le contexte expose l'instance de ViteDevServer pendant le développement, et expose le paquet de sortie Rollup pendant la construction.

    Le hook peut être asynchrone et peut retourner l'un des éléments suivants :

    • Chaîne HTML transformée
    • Un tableau d'objets descripteurs de tags ({ tag, attrs, children }) à injecter dans le HTML existant. Chaque balise peut également spécifier où elle doit être injectée (par défaut, elle est placée en amont de <head>).
    • Un objet contenant les deux comme { html, tags }

    Exemple de base:

    js
    const htmlPlugin = () => {
      return {
        nom : 'html-transform',
        transformIndexHtml(html) {
          return html.replace(
            /<titre>(.* ?)<\/titre>/,
            `<titre>Titre remplacé!</titre>`
          )
        }
      }
    }
    

    Signature complète du crochet:

    ` ```ts type IndexHtmlTransformHook = ( html : string, ctx : { path : string filename : string serveur ? ViteDevServer bundle? : import('rollup').OutputBundle chunk? : import('rollup').OutputChunk } ) => | IndexHtmlTransformResult | void | Promise<IndexHtmlTransformResult | void>

    type IndexHtmlTransformResult = | Chaîne de caractères | HtmlTagDescriptor[] | { html : string tags : HtmlTagDescriptor[] }

    interface HtmlTagDescriptor { tag : chaîne de caractères attrs ? Record<string, string | boolean> children? : string | HtmlTagDescriptor[] /**

    • par défaut : 'head-prepend'. */ injectTo ? 'head' | 'body' | 'head-prepend' | 'body-prepend'. }
    
    

handleHotUpdate

  • Type: (ctx : HmrContext) => Array<ModuleNode> | void | Promise<Array<ModuleNode> | void>

    Effectue une gestion personnalisée des mises à jour HMR. Le hook reçoit un objet de contexte avec la signature suivante :

    ts
    interface HmrContext {
     file : string
     timestamp : nombre
     modules : Array<ModuleNode>
     read : () => string | Promise<string>
     serveur : ViteDevServer
    }
    
    • modules est un tableau de modules qui sont affectés par le fichier modifié. C'est un tableau car un seul fichier peut correspondre à plusieurs modules servis (par exemple, les SFC de Vue).

    • read est une fonction de lecture asynchrone qui retourne le contenu du fichier. Ceci est fourni parce que sur certains systèmes, le callback de changement de fichier peut se déclencher trop rapidement avant que l'éditeur ne finisse de mettre à jour le fichier et fs.readFile direct retournera un contenu vide. La fonction de lecture fournie normalise ce comportement.

    Le hook peut choisir de :

    • Filtrer et réduire la liste des modules affectés afin que le HMR soit plus précis.

    • Renvoyer un tableau vide et effectuer une gestion personnalisée complète du HMR en envoyant des événements personnalisés au client :

      js
      handleHotUpdate({serveur }) {
        server.ws.send({
          type : 'custom',
          événement : 'special-update',
          données : {}
        })
        return []
      }
      

      Le code client doit enregistrer le gestionnaire correspondant à l'aide de l'API HMR (il peut être injecté par le hook transform du même plugin) :

      js
      if (import.meta.hot) {
        import.meta.hot.on('special-update', (data) => {
          // effectue une mise à jour personnalisée
        })
      }
      

Ordre des plugins

Un plugin Vite peut en plus spécifier une propriété enforce (similaire aux chargeurs webpack) pour ajuster l'ordre de ses applications. La valeur de enforce peut être soit "pre" soit "post". Les plugins résolus seront dans l'ordre suivant :

  • Alias
  • Plugins utilisateur avec enforce : 'pre'.
  • Plugins de base Vite
  • Plugins utilisateur sans valeur enforce
  • Plugins de construction Vite
  • Greffons utilisateurs avec enforce : 'post'.
  • Plugins Vite post build (minify, manifest, reporting)

Application conditionnelle

Par défaut, les plugins sont invoqués à la fois pour serve et build. Dans les cas où un plugin doit être appliqué de manière conditionnelle uniquement pendant le service ou la construction, utilisez la propriété apply pour ne les invoquer que pendant 'build' ou 'serve' :

js
function myPlugin() {
  return {
    name : 'build-only',
    apply : 'build' // ou 'serve'.
  }
}

Une fonction peut également être utilisée pour un contrôle plus précis :

js
apply(config, { command }) {
  // appliquer uniquement sur le build mais pas pour le SSR
  return command === 'build' && !config.build.ssr
}

Compatibilité avec le plugin Rollup

Un bon nombre de plugins Rollup fonctionneront directement en tant que plugins Vite (par exemple @rollup/plugin-alias ou @rollup/plugin-json), mais pas tous, car certains hooks de plugins n'ont pas de sens dans un contexte de serveur de développement dégroupé.

En général, tant qu'un plugin Rollup répond aux critères suivants, il devrait fonctionner comme un plugin Vite :

  • Il n'utilise pas le hook moduleParsed.
  • Il n'a pas de couplage fort entre les hooks de la phase bundle et ceux de la phase output.

Si un plugin Rollup n'a de sens que pour la phase de construction, alors il peut être spécifié sous build.rollupOptions.plugins à la place. Il fonctionnera de la même manière qu'un plugin Vite avec enforce : 'post' et apply : 'build'.

Vous pouvez également ajouter à un plugin Rollup existant des propriétés propres à Vite :

js
// vite.config.js
import l'exemple from 'rollup-plugin-example'.
import { defineConfig } from 'vite'.

export default defineConfig({
  plugins : [
    {
      ...exemple(),
      enforce : 'post',
      apply : " build ".
    }
  ]
})

Consultez Vite Rollup Plugins pour obtenir une liste des plugins Rollup officiels compatibles avec les instructions d'utilisation.

Normalisation des chemins

Vite normalise les chemins lors de la résolution des identifiants pour utiliser les séparateurs POSIX ( / ) tout en préservant le volume sous Windows. D'un autre côté, Rollup garde les chemins résolus intacts par défaut, donc les identifiants résolus ont des séparateurs win32 ( / ) dans Windows. Cependant, les plugins Rollup utilisent une fonction utilitaire normalizePath de @rollup/pluginutils en interne, qui convertit les séparateurs en POSIX avant d'effectuer les comparaisons. Cela signifie que lorsque ces plugins sont utilisés dans Vite, les motifs de configuration include et exclude et d'autres comparaisons similaires de chemins contre des ids résolus fonctionnent correctement.

Ainsi, pour les plugins Vite, lors de la comparaison des chemins contre les ids résolus, il est important de normaliser d'abord les chemins pour utiliser les séparateurs POSIX. Une fonction utilitaire équivalente normalizePath est exportée par le module vite.

js
import {  normalizePath } from 'vite'.

normalizePath('foo\\\bar') // 'foo/bar'.
normalizePath('foo/bar') // 'foo/bar'.

Filtrage, motif include/exclude

Vite expose la fonction @rollup/pluginutils's createFilter pour encourager les plugins et intégrations spécifiques à Vite à utiliser le modèle de filtrage standard include/exclude, qui est également utilisé dans le noyau de Vite lui-même.

Communication client-serveur

Depuis Vite 2.9, nous fournissons quelques utilitaires aux plugins pour les aider à gérer la communication avec les clients.

Communication entre le serveur et le client

Du côté du plugin, nous pouvons utiliser server.ws.send pour diffuser des événements à tous les clients :

js
// vite.config.js
export default defineConfig({
  plugins : [
    {
      // ...
      configureServer(server) {
        server.ws.send('my:greetings', { msg : 'hello' })
      }
    }
  ]
})

TIP

Nous recommandons de toujours préfixer les noms de vos événements pour éviter les collisions avec d'autres plugins.

Du côté client, utilisez hot.on pour écouter les événements :

``s // côté client if (import.meta.hot) { import.meta.hot.on('my:greetings', (data) => { console.log(data.msg) // bonjour }) }


### Client vers serveur

Pour envoyer des événements du client au serveur, nous pouvons utiliser [`hot.send`](/guide/api-hmr.html#hot-send-event-payload) :

 ```ts
// côté client
if (import.meta.hot) {
  import.meta.hot.send('my:from-client', { msg : 'Hey!' })
}

Utilisez ensuite server.ws.on pour écouter les événements du côté du serveur :

js
// vite.config.js
export default defineConfig({
  plugins : [
    {
      // ...
      configureServer(server) {
        server.ws.on('my:from-client', (data, client) => {
          console.log('Message from client:', data.msg) // Hey !
          // Réponse uniquement au client (si nécessaire)
          client.send('my:ack', { msg : 'Hi ! I got your message!' })
        })
      }
    }
  ]
})

TypeScript pour les événements personnalisés

Il est possible de typer des événements personnalisés en étendant l'interface CustomEventMap :

``s // events.d.ts Importez 'vite/types/customEvent'.

declare module 'vite/types/customEvent' { interface CustomEventMap { custom:foo' : { msg : string } // 'event-key' : charge utile } }

Documentation traduite. MIT License. (afc29b4d)