Совместимость с модульными системами CommonJS и ESM

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

В экосистеме Node.js основным способом подключения остаётся require. CryptoJS изначально проектировался под такую модель, поэтому большинство примеров использования строится вокруг CommonJS:

const CryptoJS = require("crypto-js");

После подключения доступ к алгоритмам осуществляется через пространство имён библиотеки:

const hash = CryptoJS.SHA256("data").toString();
const hmac = CryptoJS.HmacSHA256("data", "key").toString();

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

Особенности экспорта и структура пакета

CryptoJS не предоставляет полноценные ES-модули с именованными экспортами. Внутренняя архитектура опирается на объединённый объект, который собирается из множества файлов при сборке пакета.

Фактически используется схема:

  • единый экспорт-объект
  • вложенные пространства имён (AES, SHA1, enc, mode, pad)
  • отсутствие tree-shaking на уровне алгоритмов

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

Использование в ESM-проектах

В проектах с type: "module" или при использовании расширения .mjs подключение через import возможно благодаря механизму совместимости Node.js:

import CryptoJS from "crypto-js";

Такой импорт работает через интероперабельный слой CommonJS → ESM. Фактически импортируется весь объект по умолчанию.

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

import { SHA256 } from "crypto-js";

не соответствуют структуре пакета и приводят к undefined, поскольку библиотека не экспортирует отдельные символические сущности в формате ESM.

Поведение в сборщиках (Webpack, Vite, Rollup)

Современные сборщики интерпретируют CryptoJS как CommonJS-модуль. Поведение зависит от конфигурации:

Webpack

  • использует cjs интероперабельность
  • импорт превращается в единый namespace object
  • tree-shaking практически не применяется

Vite (esbuild)

  • преобразует CommonJS в ESM-обёртку
  • итоговый результат также содержит полный импорт библиотеки

Rollup

  • требует плагина @rollup/plugin-commonjs
  • после трансформации сохраняется модель “весь пакет целиком”

Ключевая проблема всех случаев — невозможность точечного импорта алгоритмов.

Отсутствие tree-shaking

Из-за архитектуры экспорта CryptoJS не поддерживает эффективное удаление неиспользуемого кода. Даже при использовании одного метода, например:

CryptoJS.MD5("data")

в бандл часто попадает вся библиотека, включая:

  • AES
  • DES
  • Rabbit
  • RC4
  • все режимы блоков и паддинги
  • кодировщики (enc.Base64, enc.Utf8)

Это следствие того, что зависимости алгоритмов связаны через общий объект и не разделены на независимые ESM-единицы.

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

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

import CryptoJS from "crypto-js";

Однако важно учитывать, что это всё равно CommonJS-объект, обёрнутый в ESM-интерфейс. Поведение сохраняется идентичным Node.js require.

Гибридная совместимость CJS ↔︎ ESM

CryptoJS демонстрирует типичный случай CJS-first библиотеки:

  • основной экспорт — CommonJS
  • ESM-доступ реализован через interop слой
  • нет нативного export синтаксиса в исходном пакете

В результате:

Сценарий Поведение
require("crypto-js") полный объект библиотеки
import CryptoJS from полный объект через interop
named import не поддерживается
tree-shaking отсутствует

Типичные проблемы интеграции

При смешивании модульных систем часто проявляются следующие эффекты:

  • undefined is not a function при попытке именованных импортов
  • увеличение размера бандла из-за полного импорта
  • дублирование CryptoJS в разных частях сборки при некорректной конфигурации
  • несовпадение поведения между dev и production сборкой

TypeScript и типизация модулей

В TypeScript подключение также идёт через дефолтный импорт:

import CryptoJS from "crypto-js";

Типы предоставляются через пакет @types/crypto-js, однако они повторяют структуру namespace-объекта, а не ESM-модулей.

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

Практическая модель взаимодействия модулей

Архитектурно CryptoJS можно рассматривать как единый глобальный namespace, который:

  • экспортируется из CommonJS
  • оборачивается в ESM при необходимости
  • не поддерживает гранулярные импорты
  • ориентирован на целостное подключение

Такой подход исторически связан с эпохой до широкого распространения ESM и современных систем tree-shaking, что объясняет текущее поведение в модульных окружениях.