Форматы вывода: esm, cjs, umd, iife, amd, system

Форматы вывода в Rollup определяют, каким образом собранный код будет представлен в итоговом бандле и в какой среде он сможет выполняться. Выбор формата напрямую связан с целевой платформой: браузер, Node.js, гибридные окружения или старые системы модульности. В конфигурации Rollup это задаётся через параметр output.format, который принимает одно из поддерживаемых значений: esm, cjs, umd, iife, amd, system.


ESM является современным стандартом модульной системы JavaScript. Он использует нативные конструкции import и export, поддерживаемые браузерами и Node.js (в режиме ES modules).

В Rollup формат esm считается базовым и наиболее предпочтительным, так как максимально сохраняет структуру исходного кода и позволяет эффективно применять tree-shaking.

Ключевые особенности ESM:

  • Поддержка статического анализа импортов и экспортов
  • Полноценный tree-shaking на уровне синтаксиса
  • Асинхронная загрузка модулей в браузере
  • Возможность работы с современными сборщиками и runtime-средами

Пример конфигурации:

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

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

Особенность Rollup заключается в том, что при esm-выводе он не обязан оборачивать код в дополнительные функции, если это не требуется для изоляции области видимости.


CJS (CommonJS)

CommonJS — модульная система, исторически используемая в Node.js. Она основана на require и module.exports.

Формат cjs применяется для обеспечения совместимости с Node.js-средой, особенно в старых версиях, где ESM не поддерживался или поддерживался частично.

Ключевые особенности CJS:

  • Синхронная загрузка модулей
  • Использование require() для импорта
  • Широкая поддержка в Node.js экосистеме
  • Менее эффективный tree-shaking по сравнению с ESM

Пример конфигурации:

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

Rollup при генерации CJS-кода преобразует export в module.exports, а import в require. Однако при сложной структуре зависимостей возможны дополнительные обёртки и ограничения, связанные с динамической природой CommonJS.

Важно учитывать, что tree-shaking в CJS значительно ограничен, поскольку анализ экспортов не является статическим в полной мере.


UMD (Universal Module Definition)

UMD представляет собой универсальный формат, предназначенный для работы как в браузере, так и в Node.js. Он объединяет подходы AMD и CommonJS с fallback на глобальную переменную.

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

Ключевые особенности UMD:

  • Поддержка браузера и Node.js
  • Автоматическое определение среды выполнения
  • Совместимость с AMD-загрузчиками
  • Создание глобальной переменной при отсутствии модульной системы

Пример конфигурации:

export default {
  input: 'src/index.js',
  output: {
    file: 'dist/bundle.umd.js',
    format: 'umd',
    name: 'MyLibrary'
  }
};

Параметр name обязателен для UMD, так как он определяет имя глобальной переменной в браузере:

window.MyLibrary

UMD-код обычно содержит обёртку вида:

  • проверка AMD (define)
  • проверка CommonJS (module.exports)
  • fallback на global object

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


IIFE (Immediately Invoked Function Expression)

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

Ключевые особенности IIFE:

  • Полная изоляция области видимости
  • Отсутствие модульной системы
  • Простое подключение через <script>
  • Подходит для небольших библиотек и утилит

Пример конфигурации:

export default {
  input: 'src/index.js',
  output: {
    file: 'dist/bundle.iife.js',
    format: 'iife',
    name: 'MyLibrary'
  }
};

Как и в UMD, параметр name определяет глобальную переменную:

window.MyLibrary

IIFE представляет собой примерно следующую структуру:

(function () {
  // весь код библиотеки
})();

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

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


AMD (Asynchronous Module Definition)

AMD — модульная система, ориентированная на асинхронную загрузку зависимостей в браузере. Наиболее известная реализация — RequireJS.

Формат amd используется значительно реже в современных проектах, но сохраняет значение для поддержки legacy-систем.

Ключевые особенности AMD:

  • Асинхронная загрузка модулей
  • Явное перечисление зависимостей
  • Поддержка браузеров без сборки
  • Использование define() как основной конструкции

Пример конфигурации:

export default {
  input: 'src/index.js',
  output: {
    file: 'dist/bundle.amd.js',
    format: 'amd'
  }
};

Rollup преобразует модули в вызовы define:

define(['dep1', 'dep2'], function (dep1, dep2) {
  return {};
});

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

Особенность AMD заключается в явной декларации зависимостей, что делает его более предсказуемым по сравнению с CommonJS, но менее удобным по сравнению с ESM.


System (SystemJS format)

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

Ключевые особенности System:

  • Поддержка динамической загрузки модулей
  • Совместимость с SystemJS loader
  • Возможность интеграции разных модульных стандартов
  • Асинхронная природа исполнения

Пример конфигурации:

export default {
  input: 'src/index.js',
  output: {
    file: 'dist/bundle.system.js',
    format: 'system'
  }
};

SystemJS использует формат System.register:

System.register([], function (exports) {
  return {
    setters: [],
    execute: function () {
      exports('default', {});
    }
  };
});

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

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


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

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

ESM обеспечивает статическую структуру, что позволяет оптимизировать код на этапе сборки. CJS ориентирован на синхронную загрузку и динамическое выполнение. UMD и IIFE обеспечивают совместимость с браузерным окружением, но отличаются уровнем абстракции: UMD универсален, IIFE минималистичен. AMD и System ориентированы на загрузчики и runtime-системы, поддерживающие асинхронную подгрузку модулей.

С точки зрения Rollup поведение tree-shaking и оптимизации лучше всего раскрывается в ESM. Остальные форматы требуют дополнительных трансформаций, что может приводить к увеличению размера итогового бандла и снижению эффективности статического анализа.


Особенности выбора формата в архитектуре сборки

Формат вывода влияет на архитектуру распределения кода. Библиотеки часто публикуются сразу в нескольких форматах:

  • ESM для современных сборщиков и bundler-aware окружений
  • CJS для Node.js совместимости
  • UMD для универсального подключения в браузере
  • IIFE для прямого подключения через script-теги
  • System для специфических runtime-систем

Rollup позволяет формировать несколько выходов одновременно:

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

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


Поведение трансформации кода при разных форматах

При генерации разных форматов Rollup выполняет различные виды трансформаций AST:

  • в ESM сохраняются import/export без обёрток
  • в CJS производится замена на require/module.exports
  • в UMD добавляются проверки окружения
  • в IIFE весь код помещается в замыкание
  • в AMD создаются define-обёртки
  • в System формируется регистрация модулей через System.register

Эти трансформации влияют на итоговую структуру файла, его размер и поведение в runtime.

Особенно важным аспектом является hoisting зависимостей: в ESM зависимости остаются статическими, в CJS и AMD они становятся динамическими, что изменяет порядок выполнения и возможности оптимизации.


Практическая роль форматов в экосистеме JavaScript

Форматы Rollup отражают эволюцию модульных систем JavaScript. ESM является финальной стадией стандартизации, тогда как CJS, AMD, UMD и IIFE представляют исторические этапы развития экосистемы.

System format занимает промежуточную позицию, обеспечивая гибкость в runtime-средах, где требуется динамическая загрузка и совместимость между различными типами модулей.

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