Поле output.dir и output.file: когда что использовать

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

  • output.file
  • output.dir

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


Поле output.file

output.file используется, когда Rollup должен создать один итоговый файл.

Пример:

export default {
    input: 'src/index.js',

    output: {
        file: 'dist/bundle.js',
        format: 'esm'
    }
};

В результате будет создан один файл:

dist/
└── bundle.js

Такой режим подходит для:

  • небольших библиотек;
  • одиночных entry points;
  • UMD/IIFE-сборок;
  • браузерных бандлов;
  • случаев, когда весь код должен находиться в одном файле.

Как работает output.file

При использовании output.file Rollup:

  1. Анализирует граф зависимостей.
  2. Выполняет tree-shaking.
  3. Объединяет модули.
  4. Генерирует единый выходной файл.

Даже если проект состоит из десятков модулей:

src/
├── index.js
├── utils.js
├── api.js
└── components/
    ├── button.js
    └── modal.js

Rollup всё равно создаст:

dist/
└── bundle.js

Пример для браузера

export default {
    input: 'src/main.js',

    output: {
        file: 'public/app.js',
        format: 'iife',
        name: 'App'
    }
};

Результат:

<script src="app.js"></script>

Внутри файла окажется весь код приложения.


Когда использовать output.file

Сборка библиотеки в один файл

output: {
    file: 'dist/my-lib.js',
    format: 'umd',
    name: 'MyLib'
}

Минифицированный production bundle

output: {
    file: 'dist/bundle.min.js',
    format: 'iife'
}

Node.js CLI-приложение

output: {
    file: 'bin/cli.js',
    format: 'cjs'
}

Простые SPA без code splitting

output: {
    file: 'dist/app.js',
    format: 'esm'
}

Ограничения output.file

Главное ограничение — невозможность полноценного code splitting.

Например:

export default {
    input: 'src/index.js',

    output: {
        file: 'dist/bundle.js',
        format: 'esm'
    }
};

Если внутри проекта используется:

import('./lazy.js');

Rollup попытается создать отдельный chunk, но поле file предполагает только один выходной файл.

В результате появится ошибка:

Invalid value for option "output.file"
when building multiple chunks

Поле output.dir

output.dir используется, когда Rollup должен создавать несколько файлов.

Пример:

export default {
    input: 'src/index.js',

    output: {
        dir: 'dist',
        format: 'esm'
    }
};

Теперь Rollup получает возможность:

  • генерировать chunks;
  • выполнять code splitting;
  • создавать динамические импорты;
  • выводить assets;
  • создавать отдельные entry bundles.

Как работает output.dir

Вместо одного файла Rollup управляет целой директорией.

Пример структуры результата:

dist/
├── index.js
├── vendor.js
├── chunk-A7F2.js
└── chunk-B91D.js

Каждый chunk создаётся автоматически.


Почему output.dir нужен для code splitting

Code splitting подразумевает наличие нескольких выходных файлов.

Пример:

// index.js
button.addEventListener('click', async () => {
    const module = await import('./dialog.js');

    module.openDialog();
});

Rollup разделит код:

dist/
├── index.js
└── dialog-83HF.js

Главный bundle будет загружать дополнительный chunk динамически.

Такое поведение невозможно с output.file.


Пример с несколькими entry points

export default {
    input: {
        main: 'src/main.js',
        admin: 'src/admin.js'
    },

    output: {
        dir: 'dist',
        format: 'esm'
    }
};

Результат:

dist/
├── main.js
├── admin.js
└── shared.js

Rollup автоматически вынесет общий код в отдельный chunk.


Сравнение output.file и output.dir

Возможность output.file output.dir
Один bundle Да Да
Несколько chunks Нет Да
Code splitting Нет Да
Dynamic import Ограничено Да
Multiple entry points Нет Да
Shared chunks Нет Да
Простая структура Да Нет
Гибкость Низкая Высокая

Типичная ошибка начинающих

Попытка совместить dynamic import с output.file.

Пример:

export default {
    input: 'src/index.js',

    output: {
        file: 'dist/bundle.js',
        format: 'esm'
    }
};

При наличии:

await import('./lazy.js');

Rollup сообщит:

To inline dynamic imports, use the
output.inlineDynamicImports option

inlineDynamicImports

Иногда требуется сохранить output.file, но при этом использовать dynamic import.

Для этого существует:

output: {
    file: 'dist/bundle.js',
    format: 'esm',
    inlineDynamicImports: true
}

Теперь Rollup встроит динамические модули внутрь одного файла.

Однако:

  • lazy loading исчезнет;
  • chunks создаваться не будут;
  • весь код окажется внутри bundle.

Когда output.dir предпочтительнее

Большие приложения

Современные frontend-приложения почти всегда используют:

  • lazy loading;
  • route splitting;
  • vendor chunks;
  • shared chunks.

Поэтому применяется:

output: {
    dir: 'dist',
    format: 'esm'
}

Многомодульные библиотеки

Например:

dist/
├── index.js
├── utils.js
├── dom.js
└── internal/

Сборка с asset-файлами

Rollup может выводить:

  • CSS;
  • изображения;
  • WASM;
  • JSON;
  • шрифты.

Всё это удобнее хранить через output.dir.


Использование с preserveModules

preserveModules сохраняет структуру модулей.

Пример:

export default {
    input: 'src/index.js',

    output: {
        dir: 'dist',
        format: 'esm',
        preserveModules: true
    }
};

Исходная структура:

src/
├── index.js
├── utils.js
└── core/
    └── api.js

Результат:

dist/
├── index.js
├── utils.js
└── core/
    └── api.js

Такой режим невозможен с output.file.


Использование output.file для UMD и IIFE

Форматы:

  • iife
  • umd

обычно предполагают один итоговый файл.

Пример:

output: {
    file: 'dist/library.js',
    format: 'umd',
    name: 'Library'
}

Причина:

  • браузер подключает один script;
  • chunks неудобны;
  • dynamic loading отсутствует.

Использование output.dir для ESM

Формат esm особенно хорошо сочетается с directory output.

Пример:

output: {
    dir: 'dist',
    format: 'esm'
}

Потому что:

  • ES-модули естественно поддерживают импорт между файлами;
  • chunks загружаются нативно;
  • tree-shaking сохраняется эффективным.

Генерация hashed chunks

При использовании output.dir можно настраивать шаблоны файлов.

Пример:

output: {
    dir: 'dist',
    format: 'esm',

    chunkFileNames: 'chunks/[name]-[hash].js',
    entryFileNames: 'entry/[name].js',
    assetFileNames: 'assets/[name]-[hash][extname]'
}

Результат:

dist/
├── chunks/
├── entry/
└── assets/

Почему output.file проще

Преимущества:

  • понятная структура;
  • один bundle;
  • простое подключение;
  • минимум конфигурации;
  • удобно для CDN.

Недостатки:

  • нет code splitting;
  • bundle может стать слишком большим;
  • хуже caching;
  • ниже гибкость.

Почему output.dir сложнее

Преимущества:

  • масштабируемость;
  • оптимизация загрузки;
  • lazy loading;
  • shared chunks;
  • современная архитектура.

Недостатки:

  • больше файлов;
  • сложнее deployment;
  • требуется корректная работа сервера;
  • сложнее debugging структуры.

Совместное использование нескольких output

Rollup позволяет создавать несколько вариантов сборки.

Пример:

export default {
    input: 'src/index.js',

    output: [
        {
            file: 'dist/library.cjs.js',
            format: 'cjs'
        },

        {
            dir: 'dist/esm',
            format: 'esm'
        }
    ]
};

Результат:

dist/
├── library.cjs.js
└── esm/
    ├── index.js
    ├── chunk.js
    └── utils.js

Практический выбор между file и dir

Использовать output.file

Если:

  • нужен один bundle;
  • отсутствует code splitting;
  • библиотека маленькая;
  • собирается UMD/IIFE;
  • нужен единый файл для CDN;
  • проект простой.

Использовать output.dir

Если:

  • применяется dynamic import;
  • используется lazy loading;
  • несколько entry points;
  • нужен code splitting;
  • проект крупный;
  • используется preserveModules;
  • требуется современная frontend-архитектура.

Внутреннее различие архитектуры Rollup

При output.file Rollup строит:

Graph -> Single Chunk -> File

При output.dir:

Graph -> Multiple Chunks -> Directory

Это фундаментальное различие механизма сборки.


Ошибка при одновременном использовании

Нельзя писать:

output: {
    file: 'dist/app.js',
    dir: 'dist'
}

Rollup выдаст ошибку конфигурации.

Причина:

  • file описывает один выходной файл;
  • dir описывает контейнер для множества файлов.

Эти стратегии несовместимы.


Поведение plugins

Многие плагины Rollup работают по-разному в зависимости от file и dir.

Например:

  • CSS extraction;
  • asset emission;
  • chunk analysis;
  • manifest generation.

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


Влияние на производительность загрузки

output.file

1 большой bundle

Плюсы:

  • меньше HTTP-запросов.

Минусы:

  • загружается весь код сразу;
  • хуже initial load;
  • сложнее кеширование отдельных частей.

output.dir

несколько специализированных chunks

Плюсы:

  • загружается только нужный код;
  • эффективнее browser cache;
  • быстрее route transitions.

Минусы:

  • больше файлов;
  • выше сложность инфраструктуры.

Современная практика

Для приложений чаще используется:

output.dir

Для библиотек:

output.file

или комбинированная стратегия:

output: [
    { file: 'dist/index.cjs.js', format: 'cjs' },
    { dir: 'dist/esm', format: 'esm' }
]

Такой подход обеспечивает:

  • совместимость;
  • оптимизацию;
  • поддержку разных экосистем;
  • удобство публикации npm-пакетов.