Множественные конфигурации в одном файле

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

Механизм множественных конфигураций основан на возможности экспортировать из конфигурационного файла не один объект, а массив:

export default [
  {
    input: 'src/index.js',
    output: {
      file: 'dist/bundle.esm.js',
      format: 'esm'
    }
  },
  {
    input: 'src/index.js',
    output: {
      file: 'dist/bundle.cjs.js',
      format: 'cjs'
    }
  }
];

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

Независимость конфигураций

При использовании массива конфигураций важно понимать, что:

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

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

Переиспользование общей логики

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

const baseConfig = {
  input: 'src/index.js',
  plugins: []
};

export default [
  {
    ...baseConfig,
    output: {
      file: 'dist/bundle.esm.js',
      format: 'esm'
    }
  },
  {
    ...baseConfig,
    output: {
      file: 'dist/bundle.cjs.js',
      format: 'cjs'
    }
  }
];

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

Динамическая генерация конфигураций

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

const formats = ['esm', 'cjs', 'umd'];

export default formats.map((format) => ({
  input: 'src/index.js',
  output: {
    file: `dist/bundle.${format}.js`,
    format
  }
}));

Динамическая генерация снижает вероятность ошибок при ручном дублировании и упрощает масштабирование сборки.

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

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

export default async () => {
  const env = await loadEnv();

  return [
    {
      input: 'src/index.js',
      output: {
        file: 'dist/bundle.js',
        format: 'esm'
      },
      plugins: [
        replace({
          __ENV__: JSON.stringify(env)
        })
      ]
    }
  ];
};

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

Разделение конфигураций по окружениям

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

const dev = {
  input: 'src/index.js',
  output: {
    file: 'dist/bundle.dev.js',
    format: 'esm',
    sourcemap: true
  }
};

const prod = {
  input: 'src/index.js',
  output: {
    file: 'dist/bundle.prod.js',
    format: 'esm',
    sourcemap: false
  }
};

export default [dev, prod];

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

Повторное использование плагинов

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

import resolve from '@rollup/plugin-node-resolve';

const createPlugins = () => [
  resolve()
];

export default [
  {
    input: 'src/a.js',
    output: { file: 'dist/a.js', format: 'esm' },
    plugins: createPlugins()
  },
  {
    input: 'src/b.js',
    output: { file: 'dist/b.js', format: 'esm' },
    plugins: createPlugins()
  }
];

Фабрика плагинов предотвращает утечку состояния между сборками и обеспечивает изоляцию.

Условная конфигурация

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

const isProd = process.env.NODE_ENV === 'production';

export default [
  {
    input: 'src/index.js',
    output: {
      file: 'dist/bundle.js',
      format: 'esm'
    }
  },
  isProd && {
    input: 'src/index.js',
    output: {
      file: 'dist/bundle.min.js',
      format: 'esm'
    }
  }
].filter(Boolean);

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

Взаимодействие с watch-режимом

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

export default [
  {
    input: 'src/index.js',
    output: {
      file: 'dist/a.js',
      format: 'esm'
    },
    watch: {
      include: 'src/**'
    }
  },
  {
    input: 'src/index.js',
    output: {
      file: 'dist/b.js',
      format: 'cjs'
    },
    watch: {
      include: 'src/**'
    }
  }
];

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

Особенности порядка выполнения

Порядок конфигураций в массиве имеет значение в контексте:

  • логирования и вывода информации;
  • последовательного выполнения build hooks;
  • потенциального взаимодействия с файловой системой.

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

Масштабирование сборки

Множественные конфигурации становятся особенно полезны при наличии:

  • библиотек с несколькими форматами вывода;
  • монорепозиториев с несколькими пакетами;
  • необходимости генерации разных сборок для разных окружений;
  • поддержки legacy и modern-версий одновременно.

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