Форматы вывода: CJS, ESM, UMD

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


Модульная система и роль формата вывода

Формат вывода определяет, каким образом собранный код будет экспортироваться и импортироваться в других проектах. В контексте JavaScript существуют три основных подхода:

  • CommonJS (CJS) — синхронная система модулей, используемая Node.js
  • ES Modules (ESM) — стандарт ECMAScript, поддерживаемый современными браузерами и Node.js
  • UMD (Universal Module Definition) — универсальная обёртка для совместимости сразу с несколькими системами

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


CommonJS (CJS)

CommonJS остаётся базовым форматом для Node.js-экосистемы. Его ключевая особенность — синхронная загрузка модулей через require.

Принцип работы

const utils = require('./utils');

module.exports = {
  sum: utils.sum
};

Parcel при генерации CJS-бандла преобразует исходные ESM-импорты в require() и module.exports.

Особенности формата

  • Поддерживается всеми версиями Node.js
  • Синхронная модель загрузки
  • Хорошо подходит для серверных библиотек
  • Не оптимален для tree-shaking на уровне потребителя

Поведение Parcel

При outputFormat: "commonjs" Parcel:

  • заменяет import на require
  • преобразует export в module.exports
  • сохраняет структуру модулей при возможности
  • может объединять зависимости в один файл или несколько чанков

ES Modules (ESM)

ESM — современный стандарт модульности, встроенный в язык JavaScript. Он используется как в браузерах, так и в современных версиях Node.js.

Пример кода

import { sum } from './utils.js';

export function calculate(a, b) {
  return sum(a, b);
}

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

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

Поведение Parcel

При outputFormat: "esmodule" Parcel:

  • сохраняет import/export без преобразования
  • генерирует оптимизированные ES-модули
  • позволяет downstream-сборщикам выполнять tree-shaking
  • поддерживает code splitting между ESM-чанками

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


UMD (Universal Module Definition)

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

  • CommonJS (Node.js)
  • AMD (RequireJS)
  • глобальные переменные в браузере

Структура UMD-обёртки

UMD обычно представляет собой функцию-обёртку следующего вида:

(function (root, factory) {
  if (typeof module === 'object' && module.exports) {
    module.exports = factory();
  } else if (typeof define === 'function' && define.amd) {
    define([], factory);
  } else {
    root.myLibrary = factory();
  }
})(this, function () {
  return {
    hello: function () {
      return 'world';
    }
  };
});

Особенности формата

  • Максимальная совместимость
  • Подходит для публичных библиотек
  • Может использоваться без сборщиков
  • Увеличенный размер из-за обёртки

Поведение Parcel

Parcel не всегда явно маркирует формат как “UMD”, но при использовании outputFormat: "global" создаётся аналогичная универсальная обёртка, ориентированная на браузерное окружение:

  • экспорт в глобальную переменную
  • поддержка подключения через <script>
  • совместимость с legacy-средами

Настройка форматов в Parcel

Parcel определяет формат вывода через конфигурацию целей (targets) в package.json.

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

{
  "name": "my-library",
  "version": "1.0.0",
  "source": "src/index.js",
  "module": "dist/module.js",
  "main": "dist/main.js",
  "targets": {
    "module": {
      "context": "browser",
      "outputFormat": "esmodule"
    },
    "main": {
      "context": "node",
      "outputFormat": "commonjs"
    },
    "umd": {
      "context": "browser",
      "outputFormat": "global"
    }
  }
}

Логика выбора формата

Parcel выбирает формат на основе:

  • поля targets
  • поля module и main
  • контекста (browser или node)
  • заданного outputFormat

Сравнение CJS, ESM и UMD

CJS

  • Используется в Node.js
  • Синхронный require
  • Простая интеграция с серверным кодом
  • Ограниченная оптимизация дерева зависимостей

ESM

  • Современный стандарт JavaScript
  • Поддержка tree-shaking
  • Асинхронная загрузка в браузере
  • Лучший выбор для библиотек нового поколения

UMD / Global

  • Универсальная совместимость
  • Работает без сборщика
  • Подходит для CDN-распространения
  • Устаревшая модель по сравнению с ESM

Влияние формата на оптимизацию

Формат вывода напрямую влияет на возможности оптимизации:

  • Tree-shaking наиболее эффективно работает в ESM
  • Code splitting проще реализуется в ESM-сборках
  • Минимизация runtime-обёрток характерна для ESM и CJS
  • UMD увеличивает размер бандла из-за универсальной логики определения среды

Parcel при ESM-режиме может оставлять зависимости “живыми” для последующей оптимизации другими инструментами.


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

Исходный код

export function add(a, b) {
  return a + b;
}

export default function multiply(a, b) {
  return a * b;
}

CJS-результат

module.exports = function multiply(a, b) {
  return a * b;
};

module.exports.add = function add(a, b) {
  return a + b;
};

ESM-результат

export function add(a, b) {
  return a + b;
}

export default function multiply(a, b) {
  return a * b;
}

UMD/Global-результат

root.myLibrary = {
  add: function (a, b) {
    return a + b;
  },
  default: function (a, b) {
    return a * b;
  }
};

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

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

  • в Node.js
  • в браузере без сборщика
  • в современных фронтенд-проектах с Vite, Webpack или Parcel

Типичная стратегия — параллельный вывод:

  • ESM как основной формат
  • CJS для обратной совместимости
  • UMD/Global для CDN-использования

Особенности совместимости

Различия между форматами влияют на:

  • способ импорта библиотеки
  • поддержку динамических импортов
  • поведение this в модулях
  • работу с зависимостями внутри пакета

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