Améliorer l'intégration avec Unplugin et Nuxt Module

- Read in english
Ressources: huchet-vue

Avoir une bibliothèque de composants robuste est un excellent point de départ, mais l'intégrer dans l'écosystème Vue.js passe à un autre niveau. Dans cet écosystème, deux outils principaux se démarquent :

  1. Unplugin Vue Components : Un plugin de bundler pour des composants à la demande qui sont auto-importés, entièrement typés et tree-shakable.
  2. Nuxt : Un framework open-source basé sur Vue.js qui simplifie et dynamise le développement web.

Bien que notre bibliothèque de composants puisse fonctionner sans intégration à ces outils, je suis fermement convaincu que l'intégration renforcera sa puissance et sa facilité d'utilisation. Je suis dévoué à offrir la meilleure expérience développeur possible, et je suis sûr que vous la trouverez inestimable.

Résolveur de Composants Unplugin Vue

Avant de construire le résolveur, il est essentiel de comprendre comment Unplugin Vue Components fonctionne. Cela clarifiera ce que fait un résolveur et sa fonctionnalité.

Tip

Si vous avez des questions, n'hésitez pas à commenter ci-dessous, et je serai heureux de vous aider.

Considérez ce composant :

vue
<script lang="ts" setup>
</script>

<template>
  <UButton label="Cliquez sur moi" />
</template>

Remarquez qu'il n'y a pas de déclaration d'importation pour le composant UButton. Unplugin Vue Components passe au crible le template et identifie les balises HTML non standards pour les trouver dans sa liste de composants. Cette liste peut être alimentée manuellement ou automatiquement peuplée à l'aide d'un résolveur.

Un résolveur est une fonction qui reçoit un nom de composant et retourne son chemin d'importation s'il correspond à la liste interne du résolveur. Cela permet l'auto-importation sélective des composants ou l'ajout de préfixes aux noms de composants.

Dans notre exemple, un résolveur pourrait résoudre le composant UButton comme suit :

ts
return {
  name: 'Button',
  from: '@nuxt/ui'
}

Unplugin Vue Components transformerait le composant en injectant la déclaration d'importation :

vue
<script lang="ts" setup>
import { Button as UButton } from '@nuxt/ui'
</script>

<template>
  <UButton label="Cliquez sur moi" />
</template>

Simple, non ? Maintenant, construisons notre résolveur.

Note

Pour approfondir votre compréhension de Vite, essayez le vite-plugin-inspector, qui vous permet d'inspecter les modifications apportées par chaque plugin.

Construction du Résolveur

Tout d'abord, rassemblez tous vos composants de bibliothèque dans un fichier src/components.ts au sein du dossier packages/huchet-vue :

bash
touch src/components.ts

Puis exportez un tableau de tous les composants :

ts
export const components = [
  'Button'
]

Pour garantir des mises à jour, créez un test qui vérifie que les composants exportés correspondent à ceux du dossier components. Créez un fichier src/index.test.ts dans packages/huchet-vue :

bash
touch src/index.test.ts

Ajoutez ce test pour vérifier les exports de composants :

ts
import { expect, it } from 'vitest'
import { components } from './components'
import * as Huchet from './index'

it('doit exposer les bons composants', () => {
  expect(Object.keys(Huchet)).toEqual(Object.values(components))
})

Ce test empêche les mises à jour manquées lorsque les composants changent.

Note

Inspiré par Radix Vue.

Ensuite, créez un src/resolver.ts pour le résolveur Unplugin Vue Components et installez-le en tant que dépendance de développement :

bash
touch src/resolver.ts && pnpm add -D unplugin-vue-components

La fonction du résolveur s'initie dans vite.config.ts, permettant aux développeurs de passer des options comme un préfixe pour les noms des composants. Elle résout les noms des composants et retourne les chemins d'importation s'ils correspondent :

ts
import type { ComponentResolver } from 'unplugin-vue-components'
import { components } from './components'

export interface ResolverOptions {
  /**
   * préfixe pour les composants utilisés dans les templates
   *
   * @defaultValue ""
   */
  prefix?: string
}

export default function (options: ResolverOptions = {}): ComponentResolver {
  const { prefix = '' } = options

  return {
    type: 'component',
    resolve: (name: string) => {
      if (name.toLowerCase().startsWith(prefix.toLowerCase())) {
        const componentName = name.substring(prefix.length)
        if (components.includes(componentName)) {
          return {
            name: componentName,
            from: '@barbapapazes/huchet-vue',
          }
        }
      }
    },
  }
}

Pour garantir que le résolveur soit construit, ajoutez-le comme point d'entrée dans vite.config.ts :

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

export default defineConfig({
  build: {
    lib: {
      entry: {
        index: resolve(__dirname, 'src/index.ts'),
        resolver: resolve(__dirname, 'src/resolver.ts'), 
      },
    },
  },
})

Enfin, exportez le résolveur dans package.json :

json
{
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.mjs"
    },
    "./resolver": { 
      "types": "./dist/resolver.d.ts", 
      "import": "./dist/resolver.mjs"
    }
  },
  "main": "./dist/index.mjs",
  "types": "dist/index.d.ts"
}

Note

Reconstruisez le projet pour vous assurer que le nouveau point d'entrée est accessible.

Utilisation du Résolveur

Avec le résolveur prêt, testons-le dans notre projet de playground :

  1. Accédez au dossier de playground :
bash
cd ../../playground/vue
  1. Installez unplugin-vue-components :
bash
pnpm add -D unplugin-vue-components
  1. Mettez à jour vite.config.ts pour utiliser le résolveur avec un préfixe U :
ts
import HuchetVueResolver from '@barbapapazes/huchet-vue/resolver'
import Vue from '@vitejs/plugin-vue'
import Components from 'unplugin-vue-components/vite'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    Vue(),
    Components({
      dts: true,
      resolvers: [
        HuchetVueResolver({
          prefix: 'U'
        }),
      ],
    })
  ],
})
  1. Ajoutez components.d.ts à .gitignore :
bash
echo "components.d.ts" >> .gitignore

Note

Ce fichier est généré par unplugin-vue-components pour aider votre IDE à comprendre les composants et à fournir une auto-complétion.

  1. Mettez à jour src/App.vue pour utiliser le composant UButton sans l'importer :
vue
<template>
  <div class="flex gap-6 p-6">
    <UButton label="Hello World" />
    <UButton label="Hello World" variant="outline" />
  </div>
</template>

Voilà ! Le composant UButton est automatiquement importé pour plus de facilité d'utilisation.

Module Nuxt

Nuxt transforme le développement web avec Vue.js en fournissant une configuration étendue et un système de modules. Construire un module Nuxt pour notre bibliothèque de composants nécessite peu d'efforts et élimine la configuration pour les développeurs. Plongeons-y.

Construction du Module

Commencez par créer un fichier nuxt.ts dans le dossier packages/huchet-vue :

bash
touch nuxt.ts

Installez les paquets Nuxt nécessaires ainsi que Tailwind CSS :

bash
pnpm add -D @nuxt/kit @nuxt/schema && pnpm add tailwindcss@next @tailwindcss/vite@next @tailwindcss/postcss@next

Ensuite, créez le module :

ts
import type { } from '@nuxt/schema' // Obligatoire pour éviter un bug lors de la construction
import { addComponent, addVitePlugin, defineNuxtModule } from '@nuxt/kit'

import { components } from './components'

export interface ModuleOptions {
  prefix: string
}

export default defineNuxtModule<ModuleOptions>({
  meta: {
    name: '@barbapapazes/huchet-vue',
    configKey: 'huchet',
    compatibility: {
      nuxt: '>=3.0.0',
    },
  },
  defaults: {
    prefix: '',
  },
  async setup(options, nuxt) {
    if (nuxt.options.builder === '@nuxt/vite-builder') {
      const Tailwind = await import('@tailwindcss/vite').then(r => r.default)
      addVitePlugin(Tailwind())
    }
    else {
      nuxt.options.postcss.plugins['@tailwindcss/postcss'] = {}
    }

    for (const component of components) {
      addComponent({
        name: `${options.prefix}${component}`,
        export: component,
        filePath: '@barbapapazes/huchet-vue',
      })
    }
  },
})

Ce plugin simplifie la configuration de Tailwind CSS, utilisant soit Vite soit PostCSS en fonction du constructeur Nuxt.

La deuxième partie auto-importe les composants dans le système Nuxt en utilisant le framework Unimport, permettant la personnalisation des préfixes comme le résolveur Unplugin Vue Components.

Inspiré par Radix Vue Nuxt module et Nuxt UI 3 module.

Ajoutez une entrée dans vite.config.ts et exportez-la dans package.json :

ts
import { resolve } from 'node:path'

export default defineConfig({
  build: {
    lib: {
      entry: {
        index: resolve(__dirname, 'src/index.ts'),
        resolver: resolve(__dirname, 'src/resolver.ts'),
        nuxt: resolve(__dirname, 'src/nuxt.ts'), 
      },
    },
    rollupOptions: {
      external: ['vue', '@nuxt/kit', '@tailwindcss/vite'], 
    },
  },
})

Assurez-vous d'externaliser @nuxt/kit et @tailwindcss/vite.

json
{
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.mjs"
    },
    "./resolver": {
      "types": "./dist/resolver.d.ts",
      "import": "./dist/resolver.mjs"
    },
    "./nuxt": {
      "types": "./dist/nuxt.d.ts",
      "import": "./dist/nuxt.mjs"
    }
  },
  "main": "./dist/index.mjs",
  "types": "dist/index.d.ts"
}

Utilisation du Module

Avec le module Nuxt en place, mettons en place un moyen de tester sa fonctionnalité. Nous allons créer un nouveau playground Nuxt, installer le package et le configurer.

  1. Créez un nouveau projet Nuxt depuis le répertoire racine :
bash
npx nuxi init ./playground/nuxt

Note

Modifiez le script postinstall en dev:prepare dans le package.json de Nuxt pour éviter les erreurs après une installation de package. Renommez-le car prepare est réservé par npm.

  1. Installez la bibliothèque de composants :
bash
cd ./playground/nuxt && pnpm add ../../packages/huchet-vue
  1. Dupliquez le App.vue du playground Vue vers le playground Nuxt :
bash
cp ../vue/src/App.vue ./app.vue
  1. Ajoutez le module dans nuxt.config.ts :
ts
export default defineNuxtConfig({
  modules: ['@barbapapazes/huchet-vue/nuxt'],
  css: ['~/assets/css/main.css'],
  huchet: {
    prefix: 'U',
  },
  compatibilityDate: '2024-11-01',
  devtools: { enabled: true },
})

Note

Incluez le fichier CSS en utilisant la propriété css pour faire fonctionner Tailwind CSS.

  1. Créez le fichier CSS dans assets/css/main.css :
bash
mkdir -p assets/css && touch assets/css/main.css && echo "@import 'tailwindcss';" >> assets/css/main.css
  1. Lancez le projet Nuxt :
bash
pnpm dev

Voila ! Vous pouvez désormais utiliser la bibliothèque de composants dans un projet Nuxt de manière fluide, avec des composants auto-importés et un Tailwind CSS pleinement fonctionnel.

L'expérience développeur Nuxt, enrichie par le système de modules, simplifie considérablement la vie des développeurs. Je suis sûr que vous l'apprécierez aussi.

Photo de profil d'Estéban

Merci de me lire ! Je m'appelle Estéban, et j'adore écrire sur le développement web.

Je code depuis plusieurs années maintenant, et j'apprends encore de nouvelles choses chaque jour. J'aime partager mes connaissances avec les autres, car j'aurais aimé avoir accès à des ressources aussi claires et complètes lorsque j'ai commencé à apprendre la programmation.

Si vous avez des questions ou souhaitez discuter, n'hésitez pas à commenter ci-dessous ou à me contacter sur Bluesky, X, et LinkedIn.

J'espère que vous avez apprécié cet article et appris quelque chose de nouveau. N'hésitez pas à le partager avec vos amis ou sur les réseaux sociaux, et laissez un commentaire ou une réaction ci-dessous—cela me ferait très plaisir ! Si vous souhaitez soutenir mon travail, vous pouvez me sponsoriser sur GitHub !