@rollup/plugin-virtual

Назначение плагина и область применения

Плагин @rollup/plugin-virtual предназначен для создания виртуальных модулей, которые не существуют в файловой системе, но могут быть импортированы в сборке Rollup как обычные ES-модули. Это механизм позволяет динамически подставлять содержимое модулей прямо в конфигурации сборщика без необходимости создавать физические файлы.

Основная идея заключается в том, чтобы расширить систему модулей Rollup за пределы файловой системы и дать возможность определять зависимости “на лету”. Виртуальные модули особенно полезны в сценариях генерации кода, тестирования, условной компоновки и интеграции с внешними источниками данных.

Принцип работы виртуальных модулей

Rollup по умолчанию работает с файловой системой: каждый import соответствует реальному файлу. Плагин виртуальных модулей перехватывает процесс разрешения импортов и подменяет его собственными данными.

Ключевые этапы обработки:

  • Перехват идентификатора модуля на этапе resolveId
  • Сопоставление идентификатора с заранее определённым виртуальным модулем
  • Возврат специального ID, который Rollup считает “разрешённым”
  • Генерация содержимого модуля на этапе load

В результате импорт вида:

import data from 'virtual:config';

превращается в полноценный модуль, код которого был сгенерирован в памяти сборщика.

Базовое использование

Стандартная конфигурация плагина выглядит следующим образом:

import virtual from '@rollup/plugin-virtual';

export default {
  input: 'src/main.js',
  plugins: [
    virtual({
      'virtual:config': `
        export const name = "app";
        export const version = "1.0.0";
      `
    })
  ],
  output: {
    format: 'esm',
    file: 'dist/bundle.js'
  }
};

В данном примере создаётся виртуальный модуль virtual:config, экспортирующий две переменные. Он может быть импортирован в любом месте проекта так же, как обычный файл.

Механика сопоставления идентификаторов

Плагин использует словарь, где ключом выступает строка-маркер модуля, а значением — строка с JavaScript-кодом.

Особенности сопоставления:

  • ключи могут быть произвольными строками
  • нет обязательного использования префикса virtual:, но он является распространённой практикой
  • совпадение должно быть точным
  • каждый ключ соответствует одному модулю

При попытке импорта Rollup передаёт имя модуля в resolveId. Плагин проверяет наличие совпадения и возвращает идентификатор, который затем используется в load.

Генерация динамического содержимого

Хотя плагин часто используется со статическими строками, его сила раскрывается при генерации содержимого программным способом.

Пример динамической генерации:

virtual({
  'virtual:env': (() => {
    const env = process.env.NODE_ENV;

    return `
      export const mode = "${env}";
      export const timestamp = ${Date.now()};
    `;
  })()
})

Здесь код формируется во время инициализации сборщика, что позволяет внедрять параметры окружения без дополнительных loader-ов.

Использование функций вместо строк

Плагин поддерживает более гибкий подход, когда значение модуля задаётся функцией:

virtual({
  'virtual:api': () => {
    const baseUrl = 'https://api.example.com';

    return `
      export const baseUrl = "${baseUrl}";
      export const fetchUsers = () => fetch("${baseUrl}/users");
    `;
  }
})

Такой подход позволяет:

  • использовать внешние зависимости
  • выполнять вычисления перед сборкой
  • интегрироваться с конфигурациями приложения

Взаимодействие с системой кеширования Rollup

Rollup активно кеширует результаты модулей для ускорения пересборки. Виртуальные модули влияют на этот процесс следующим образом:

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

Это особенно важно учитывать при использовании Date.now(), случайных чисел или данных из внешних источников.

Использование виртуальных модулей для конфигурации приложения

Виртуальные модули часто применяются для централизованной конфигурации:

virtual({
  'virtual:config': `
    export const config = {
      apiUrl: "https://api.example.com",
      debug: true,
      retries: 3
    };
  `
})

Преимущество такого подхода заключается в отсутствии необходимости создавать отдельные конфигурационные файлы, а также в возможности переопределять конфигурацию на этапе сборки.

Интеграция с TypeScript

При использовании TypeScript возникает проблема отсутствия типов для виртуальных модулей. Решение заключается в декларации модулей вручную:

declare module 'virtual:config' {
  export const name: string;
  export const version: string;
}

Это позволяет TypeScript корректно обрабатывать импорты виртуальных модулей и обеспечивать автодополнение.

Использование в архитектуре плагинов

@rollup/plugin-virtual часто применяется как вспомогательный инструмент в более сложных плагинах. Например:

  • генерация маршрутов в SPA
  • внедрение метаданных сборки
  • создание mock-данных для тестов
  • подмена API-слоёв
  • генерация индексных модулей

В архитектуре Rollup он выступает как базовый слой для создания “искусственных” точек входа.

Ограничения и особенности поведения

Несмотря на гибкость, виртуальные модули имеют ряд ограничений:

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

Также важно учитывать, что виртуальный модуль существует только в контексте сборки и не может быть использован в runtime напрямую.

Взаимодействие с другими плагинами Rollup

Порядок подключения плагинов влияет на поведение виртуальных модулей. Обычно @rollup/plugin-virtual размещается ближе к началу цепочки, чтобы:

  • корректно перехватывать resolveId
  • не конфликтовать с alias-плагинами
  • обеспечивать предсказуемую обработку импортов

При совместном использовании с плагинами трансформации (Babel, TypeScript) виртуальные модули обрабатываются так же, как обычные файлы после стадии загрузки.

Практические сценарии применения

Виртуальные модули используются в случаях, когда требуется:

  • генерация кода на основе окружения
  • создание тестовых заглушек без файлов
  • формирование конфигураций сборки
  • динамическая подмена зависимостей
  • ускорение прототипирования без структуры проекта

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