В стандартном режиме сборки Rollup стремится максимально объединить граф модулей в единый или несколько оптимально разрезанных бандлов. Каждый модуль превращается в часть итогового графа, где границы исходных файлов перестают существовать как физическая структура. Такой подход удобен для приложений, но становится проблемой при публикации библиотек, особенно если важно сохранить модульную архитектуру.
output.preserveModules изменяет это поведение: вместо
объединения модулей Rollup сохраняет исходную файловую структуру,
превращая каждый модуль в отдельный файл в выходной директории. При этом
сохраняются зависимости, имена и относительная структура проекта.
При включении режима:
export default {
input: 'src/index.js',
output: {
dir: 'dist',
format: 'esm',
preserveModules: true
}
}
Rollup перестаёт рассматривать граф модулей как материал для слияния. Вместо этого:
src повторяется в
dist;Фактически формируется не бандл, а компиляция файловой системы с применёнными трансформациями Rollup.
В обычной сборке:
При preserveModules: true:
Это принципиально меняет цель сборки: вместо доставки «готового пакета кода» создаётся «переносимая версия исходной архитектуры».
Основной сценарий — публикация библиотек.
Когда библиотека предоставляет:
/utils/date, /utils/math);import x from 'lib/feature');сохранение структуры позволяет пользователю импортировать только нужные части без потери семантики путей.
Для UI-библиотек важно:
preserveModules делает структуру предсказуемой:
dist/
button.js
modal.js
utils/
classnames.js
При использовании Node.js ESM:
Tree-shaking не исчезает, но его характер меняется.
Вместо глобального анализа всего приложения:
Это означает, что:
На практике preserveModules используется почти
исключительно с ESM:
output: {
dir: 'dist',
format: 'esm',
preserveModules: true
}
Причина в том, что:
При попытке использовать с другими форматами могут возникать ограничения или деградация смысла результата.
preserveModules несовместим с единым файлом вывода.
Причина проста: невозможно сохранить множество файлов в один
output.file.
Поэтому используется:
output.dir — обязательный параметр;Это превращает сборку из генерации файла в генерацию каталога.
Rollup старается сохранить относительную структуру входных файлов:
src/
components/
button.js
utils/
format.js
становится:
dist/
components/
button.js
utils/
format.js
Но поведение может корректироваться через дополнительные настройки.
Чтобы избежать избыточной вложенности src в
dist, используется:
output: {
dir: 'dist',
format: 'esm',
preserveModules: true,
preserveModulesRoot: 'src'
}
Это позволяет:
dist зеркалом логической структуры, а не
файловой системы разработки;При сохранении модулей Rollup переписывает импорты:
Например:
import { helper } from '../utils/helper.js'
в итоговой структуре остаётся валидным, но путь может быть пересчитан относительно нового расположения файла.
Несмотря на полезность, режим имеет ряд ограничений:
Также важно учитывать, что:
При использовании preserveModules структура проекта
становится частью API.
Типичная организация:
src/
index.js
core/
hooks/
utils/
Именно эта структура:
Поэтому проектирование директорий перестаёт быть внутренним вопросом и становится контрактом.
Хотя preserveModules внешне напоминает code splitting,
логика противоположная:
В первом случае результат оптимизирован под загрузку, во втором — под структуру и переиспользование.
При включённом режиме Rollup выполняет следующий цикл:
В результате получается не бандл, а структурированный набор ESM-модулей, готовый для публикации и точечного импорта.