Создание Vite-плагина для автоматической генерации index.js компонентов

от автора

в
Время чтения: 3 мин.

В этой статье я расскажу о процессе создания Vite-плагина, который автоматически генерирует файл index.js для экспорта Vue-компонентов, и разберу его код.

Назначение плагина

Плагин GenerateComponentsIndex решает несколько важных задач в проекте на Vue.js:

  1. Автоматизация экспорта компонентов — вместо ручного добавления экспортов в index.js при создании новых компонентов, плагин делает это автоматически.
  2. Поддержка структуры папок — позволяет гибко настраивать именование компонентов (с включением или исключением имени родительской папки).
  3. Генерация имен в PascalCase — автоматически преобразует имена файлов в формат PascalCase, который является стандартом для именования Vue-компонентов.
  4. Горячее обновление — отслеживает изменения в папке компонентов и автоматически обновляет index.js.

Процесс создания плагина

1. Импорт необходимых модулей

import fs from 'fs';
import path from 'path';
import { normalizePath } from 'vite';
  • fs — модуль для работы с файловой системой Node.js
  • path — для работы с путями файлов
  • normalizePath из Vite — для нормализации путей (приведение к единому формату)

2. Основная структура плагина

Плагин экспортирует функцию, которая принимает объект с настройками:

export default function GenerateComponentsIndex({
  includeFolderName = false, // Не добавлять название папки к имени компонента
  pascalCase = true,         // Преобразовывать в PascalCase
} = {}) {
  let config;

  return {
    name: 'generate-components-index',
    // ...остальная реализация
  };
}

Настройки:

  • includeFolderName — если true, добавляет имя родительской папки к имени компонента
  • pascalCase — если true, преобразует имена в PascalCase

3. Хуки жизненного цикла Vite

Плагин использует два хука:

  1. configResolved — сохраняет конфигурацию Vite:
configResolved(resolvedConfig) {
  config = resolvedConfig;
},
  1. configureServer — основной хук, который выполняется при запуске dev-сервера:
configureServer(server) {
  const componentsDir = path.join(config.root, 'src/components');
  const indexPath = path.join(componentsDir, 'index.js');
  // ...реализация
}

4. Рекурсивный поиск Vue-файлов

Функция findVueFiles обходит все подпапки и находит файлы .vue:

const findVueFiles = (dir) => {
  const files = fs.readdirSync(dir);
  let result = [];

  files.forEach((file) => {
    const fullPath = path.join(dir, file);
    const stat = fs.statSync(fullPath);

    if (stat.isDirectory()) {
      result.push(...findVueFiles(fullPath));
    } else if (file.endsWith('.vue')) {
      result.push(fullPath);
    }
  });

  return result;
};

5. Генерация index.js

Функция generateIndex создает содержимое файла index.js:

const generateIndex = () => {
  try {
    const vueFiles = findVueFiles(componentsDir);
    const exports = vueFiles.map((filePath) => {
      const relativePath = path.relative(componentsDir, filePath);
      const importPath = `./${relativePath.replace(/\\/g, '/')}`;

      let componentName = path.basename(relativePath, '.vue');

      // Добавление имени папки при необходимости
      if (includeFolderName) {
        const folderPath = path.dirname(relativePath);
        if (folderPath !== '.') {
          const folderName = folderPath.split(/[\\/]/).pop();
          componentName = `${folderName}-${componentName}`;
        }
      }

      // Преобразование в PascalCase
      if (pascalCase) {
        componentName = componentName
          .split(/[-_]/)
          .map(part => part.charAt(0).toUpperCase() + part.slice(1))
          .join('');
      }

      return `export { default as ${componentName} } from '${importPath}';`;
    }).join('\n');

    fs.writeFileSync(indexPath, exports);
    console.log('🔄 Updated components/index.js');
  } catch (err) {
    console.error('⚠️ Error generating index:', err.message);
  }
};

6. Отслеживание изменений

Плагин устанавливает watcher для отслеживания изменений в папке компонентов:

generateIndex();
const watcher = fs.watch(
  componentsDir,
  { recursive: true },
  (eventType, filename) => {
    if (filename && filename.endsWith('.vue')) {
      generateIndex();
    }
  }
);

server.httpServer?.once('close', () => watcher.close());

Пример использования

В vite.config.js:

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import GenerateComponentsIndex from './plugins/generate-components-index';

export default defineConfig({
  plugins: [
    vue(),
    GenerateComponentsIndex({
      includeFolderName: true,
      pascalCase: true
    })
  ]
});

Результат работы

Для структуры:

src/components/
  Button/
    PrimaryButton.vue
    SecondaryButton.vue
  Modal.vue

Плагин сгенерирует index.js:

export { default as ButtonPrimaryButton } from './Button/PrimaryButton.vue';
export { default as ButtonSecondaryButton } from './Button/SecondaryButton.vue';
export { default as Modal } from './Modal.vue';

Заключение

Этот плагин значительно упрощает работу с компонентами в больших проектах, автоматизируя рутинные задачи. Он демонстрирует возможности расширения функциональности Vite через создание собственных плагинов и может быть адаптирован под конкретные нужды проекта.


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Сколько будет 1 + 3?