Модульная система ECMAScript Modules (ESM) определяет статическую
структуру зависимостей JavaScript-кода через конструкции
import и export. В отличие от CommonJS, где
загрузка модулей происходит динамически через require, ESM
фиксирует граф зависимостей на этапе парсинга. Это свойство критично для
инструментов трансформации, включая SWC, поскольку позволяет проводить
анализ кода до его исполнения.
Ключевые характеристики ESM:
await;
SWC опирается на эти свойства при трансформации, сохраняя семантическую эквивалентность исходного кода при изменении целевой модульной системы.
SWC реализует конвейер преобразования AST, где этап работы с модулями встроен в стадия трансформации:
module.type.
При этом SWC не ограничивается простым синтаксическим переписыванием
import/export. Он учитывает:
Режим сохранения ESM-структуры применяется, когда исходный код должен остаться максимально близким к оригинальному модульному графу.
В конфигурации SWC это выражается через:
module.type: “es6” или “esm” (в зависимости от
конфигурационного слоя);
import/export в
require/module.exports;
Поведение в этом режиме:
import остается неизменным;
export сохраняется как есть;
import() не трансформируются;
Особенность SWC заключается в том, что даже при сохранении структуры он может выполнять минимальные корректировки:
Одним из наиболее часто используемых сценариев является преобразование модулей ECMAScript в CommonJS для сред выполнения без нативной поддержки ESM.
При включении module.type: “commonjs” SWC выполняет
следующие преобразования:
import fs from "fs";
превращается в:
const fs = require("fs");
Для именованных импортов:
import { readFile } from "fs";
становится:
const { readFile } = require("fs");
export const value = 1;
преобразуется в:
const value = 1;
exports.value = value;
или через объект module.exports при default-экспортах:
export default function () {}
→
module.exports = function () {};
Одной из сложностей трансформации ESM является сохранение live bindings — поведения, при котором импортированная переменная отражает актуальное значение экспортера.
SWC при генерации CommonJS-кода может использовать промежуточные геттеры или ссылки на объект экспорта:
exports.__esModule = true;
exports.value = void 0;
Object.defineProperty(exports, "value", {
enumerable: true,
get: () => value
});
Это позволяет приблизить поведение CommonJS-кода к ESM-модели.
Конструкция динамического импорта:
import("module");
в ESM остается функцией, возвращающей Promise.
При трансформации в CommonJS SWC сохраняет асинхронную природу через обертку:
Promise.resolve().then(() => require("module"));
Такой подход обеспечивает:
import().
Важной задачей является сохранение зависимости между модулями при трансформации.
SWC не выполняет bundling на уровне трансформации, но поддерживает структуру:
Это особенно важно при использовании ESM → CJS трансформации в больших проектах, где граф модулей должен оставаться стабильным.
При смешанном окружении возникает необходимость в межмодульной совместимости.
SWC добавляет вспомогательные конструкции:
Object.defineProperty(exports, "__esModule", { value: true });
import pkg from "cjs-module";
→
const pkg = require("cjs-module");
const _default = pkg.__esModule ? pkg.default : pkg;
Это позволяет корректно обрабатывать:
module.exports;
default export;
Хотя tree-shaking обычно выполняется на уровне bundler-а, SWC может участвовать в подготовке кода:
Пример:
export { a } from "./a";
может быть развернут в промежуточную форму, удобную для последующей оптимизации:
Object.defineProperty(exports, "a", {
enumerable: true,
get: function () {
return require("./a").a;
}
});
SWC поддерживает несколько стратегий генерации:
import/export;
require/module.exports;
System.register;
Поведение трансформации модулей зависит от набора параметров:
module.type
module.strict
module.noInterop
jsc.target
Комбинации этих параметров определяют:
SWC при трансформации может вставлять вспомогательные функции:
__importDefault
__importStar
__exportStar
Они обеспечивают корректную эмуляцию ESM-поведения в CJS-среде.
Пример:
var __importDefault = function (mod) {
return mod && mod.__esModule ? mod : { default: mod };
};
Конструкции вида:
export * from "./module";
в CommonJS требуют особой обработки:
Object.keys(require("./module")).forEach(function (key) {
if (key !== "default") {
exports[key] = require("./module")[key];
}
});
Такая трансформация сохраняет:
При наличии:
await fetchData();
SWC в ESM-режиме сохраняет выражение как есть, тогда как в CommonJS может потребоваться оборачивание в async IIFE:
(async function () {
await fetchData();
})();
Это влияет на порядок выполнения модулей и требует учета зависимости между файлами.
Цель трансформации ESM в SWC заключается в сохранении:
Система преобразования ориентируется не только на синтаксис, но и на семантическую модель модулей, что делает результат пригодным для использования в различных окружениях без изменения исходной архитектуры зависимостей.