Création d'un loader webpack pour la séparation des composants Vue

Introduction aux loaders webpack

Les loaders webpack sont des fonctions fondamentales qui transforment des fichiers source en modules JavaScript ou JSON reconnus par webpack. Ils permettent d'étendre les fonctionnalités de base de webpack pour gérer différents types de fichiers, comme TypeScript, SCSS, ou des fichiers Vue, en les convertissant en modules compatibles.

Webpack fonctionne comme un bundler de modules statiques, construisant un graphe de dépendances pour regrouper tous les fichiers nécessaires en bundles optimisés. Les loaders interviennent dans ce processus pour effectuer des opérations telles que la compilation de TypeScript en JavaScript, l'optimisation des fichiers, la division du code, et bien plus.

Scénario pour les composatns Vue

Dans les projets Vue.js, les composants sont souvent écrits dans des fichiers .vue uniques contenant le template, le script et le style. Cependant, pour améliorer la lisibilité, on peut séparer ces parties en fichiers distincts. Par exemple, un composant MyComponent pourrait être divisé en MyComponent.vue (template), MyComponent.vue.ts (script TypeScript), et MyComponent.vue.scss (style SCSS).

Pour gérer cette séparation, nous allons créer un loader webpack personnalisé qui fusionne ces fichiers séparés en un seul fichier .vue avant de le passer à vue-loader. Cela permet de maintenir la séparation des préoccupations tout en conservant la compatibilité avec l'écosystème Vue.

Mise en place de l'environnement

Nous partons d'un environnement Vue et TypeScript de base. Vous pouvez cloner le dépôt suivant et installer les dépendances :

git clone https://github.com/WindrunnerMax/webpack-simple-environment.git
git checkout webpack--vue-cli
yarn install

Exécutez yarn dev pour vérifier que l'application fonctionne. La structure des fichiers ressemble à ceci :

├── src
│   ├── components
│   │   ├── tab-a.vue
│   │   └── tab-b.vue
│   ├── views
│   │   └── framework.vue
│   └── App.vue
├── webpack.config.js
└── ...

Écriture du loader personnalisé

Le loader recevra un fichier .vue comme entrée (une chaîne de caractères) et retournera une chaîne modifiée. Nous allons inspecter le répertoire du fichier source pour chercher des fichiers associés (comme les fichiers .ts et .scss) et les intégrer au contenu.

Tout d'abord, créez un fichier vue-separator-loader.js à la racine du projet. Ensuite, configurez webpack pour utiliser ce loader avant vue-loader :

// Dans webpack.config.js
module: {
  rules: [
    {
      test: /\.vue$/,
      use: [
        'vue-loader',
        {
          loader: './vue-separator-loader',
          options: {
            styleExtensions: ['scss', 'css'],
            scriptExtensions: ['ts', 'js'],
          },
        },
      ],
    },
  ],
}

Les options spécifient les extensions de fichiers à rechercher pour les scripts et les styles. Le loader s'exécute de manière asynchrante pour lire les fichiers sans bloquer le processus.

Implémentation du loader

Voici le code du loader avec des noms de variables modifiés et une logique légèrement restructurée :

const fs = require('fs');
const path = require('path');
const { promisify } = require('util');
const loaderUtils = require('loader-utils');

const lireRepertoire = promisify(fs.readdir);
const lireFichier = promisify(fs.readFile);

module.exports = async function(sourceVue) {
  const callback = this.async();
  const repertoireCourant = this.context;
  const nomFichierVue = path.basename(this.resourcePath);

  const options = loaderUtils.getOptions(this) || {};
  const extensionsStyle = options.styleExtensions || [];
  const extensionsScript = options.scriptExtensions || [];

  // Construire des expressions régulières pour les fichiers associés
  const regexStyles = new RegExp(
    extensionsStyle.map(ext => `${nomFichierVue.replace('.vue', '')}\\.vue\\.${ext}$`).join('|')
  );
  const regexScripts = new RegExp(
    extensionsScript.map(ext => `${nomFichierVue.replace('.vue', '')}\\.vue\\.${ext}$`).join('|')
  );

  let cheminStyle = null;
  let cheminScript = null;

  try {
    const fichiers = await lireRepertoire(repertoireCourant);
    fichiers.forEach(fichier => {
      if (regexStyles.test(fichier)) cheminStyle = path.join(repertoireCourant, fichier);
      if (regexScripts.test(fichier)) cheminScript = path.join(repertoireCourant, fichier);
    });

    // Ajouter le script si nécessaire
    if (cheminScript && !/<script>/.test(sourceVue)) {
      const extensionScript = path.extname(cheminScript).slice(1);
      if (extensionScript) {
        const contenuScript = await lireFichier(cheminScript, 'utf8');
        const baliseScript = `<script${extensionScript !== 'js' ? ` lang="${extensionScript}"` : ''}>\n${contenuScript}\n</script>`;
        sourceVue += `\n${baliseScript}`;
      }
    }

    // Ajouter le style si nécessaire
    if (cheminStyle && !/<style>/.test(sourceVue)) {
      const extensionStyle = path.extname(cheminStyle).slice(1);
      if (extensionStyle) {
        const contenuStyle = await lireFichier(cheminStyle, 'utf8');
        const estScoped = /\/\/\s*scoped\n/.test(contenuStyle);
        const baliseStyle = `<style${extensionStyle !== 'css' ? ` lang="${extensionStyle}"` : ''}${estScoped ? ' scoped' : ''}>\n${contenuStyle}\n</style>`;
        sourceVue += `\n${baliseStyle}`;
      }
    }

    callback(null, sourceVue);
  } catch (erreur) {
    callback(erreur);
  }
};

Ce code recherche des fichiers avec des extensions spécifiées, les lit et les injecte dans le fichier .vue original si les balises correspondantes sont absentes. Le loader gère les styles scoped via des commentaires.

Conclusion technique

Les loaders webpack offrent une flexibilité puissente pour adapter le processus de construction aux besoins spécifiques. En personnlaisant un loader, on peut automatiser des tâches complexes, comme la fusion de fichiers pour les composants Vue, améliorant ainsi la maintenabilité du code.

Références

Étiquettes: webpack loader Vue.js TypeScript SCSS

Publié le 28 juin à 21h48