CommonJS остаётся одной из ключевых модульных систем в экосистеме
Node.js, несмотря на доминирование ESM в современном JavaScript. SWC,
как высокопроизводительный транспайлер, реализует трансформацию модулей
CommonJS через преобразование синтаксиса import/export в
классическую форму require/module.exports, а также через
эмуляцию поведения ESM в CJS-окружении.
Базовая задача трансформации заключается не только в механической замене синтаксиса, но и в сохранении семантики модулей: порядка инициализации, кеширования, живых связей экспортов и корректной обработки циклических зависимостей.
При включении режима CommonJS SWC преобразует ESM-конструкции в следующие примитивы:
import → require
export → exports или
module.exports
export default → module.exports.default или
прямое присваивание module.exports
Ключевая сложность заключается в том, что ESM и CommonJS имеют различную модель исполнения:
SWC компенсирует эти различия через генерацию вспомогательных обёрток и функций интеропа.
import fs from "fs";
Преобразуется в:
const fs = require("fs");
Такой случай является наиболее простым и не требует дополнительных обёрток.
import { readFile } from "fs";
Трансформируется в:
const { readFile } = require("fs");
Здесь сохраняется семантика выбора конкретных экспортов, однако исчезает концепция “живых” связей: значение фиксируется в момент require.
import { readFile as rf } from "fs";
Результат:
const { readFile: rf } = require("fs");
import * as fs from "fs";
SWC генерирует:
const fs = require("fs");
Однако в зависимости от конфигурации может добавляться интероп-обёртка:
const fs = _interopRequireWildcard(require("fs"));
export const a = 1;
export function foo() {}
Преобразуется в:
const a = 1;
function foo() {}
exports.a = a;
exports.foo = foo;
При этом SWC может группировать экспорты в конце файла для сохранения структуры исполнения.
const x = 10;
export { x };
Результат:
const x = 10;
exports.x = x;
export { x as y };
exports.y = x;
export default является наиболее сложным случаем, так как
CommonJS не имеет прямого аналога.
export default 42;
Преобразуется в:
module.exports = 42;
export default function foo() {}
Результат:
function foo() {}
module.exports = foo;
export default foo;
export const bar = 1;
SWC в этом случае создаёт структуру:
module.exports = foo;
exports.bar = 1;
exports.__esModule = true;
Флаг __esModule используется для обозначения того, что
модуль изначально был ESM и позволяет корректно работать с интеропом в
системах, ожидающих ESModule-совместимость.
SWC генерирует вспомогательные функции для согласования различий между системами модулей.
Используется при импорте default-экспорта из CJS:
import foo from "cjs-module";
Преобразуется концептуально в:
const _mod = require("cjs-module");
const foo = _mod && _mod.__esModule ? _mod.default : _mod;
Эта проверка позволяет корректно извлекать default даже из модулей, которые были транспилированы из ESM.
Используется для import * as:
import * as ns from "mod";
Логика:
__esModule), возвращается как есть
default и перечислением
свойств
Пример логики:
function _interopRequireWildcard(obj) {
if (obj && obj.__esModule) return obj;
const newObj = {};
if (obj != null) {
for (const key in obj) {
newObj[key] = obj[key];
}
}
newObj.default = obj;
return newObj;
}
CommonJS использует кеш модулей, что позволяет разрешать циклы, но с частично инициализированными значениями.
SWC сохраняет оригинальную модель Node.js:
require() внутри цикла возвращает текущую версию
exports
Пример проблемы:
// a.js
const b = require("./b");
exports.a = 1;
// b.js
const a = require("./a");
exports.b = 2;
В момент выполнения один из модулей может получить неполный
exports, что является допустимым поведением CommonJS и
сохраняется при трансформации.
const mod = require(someVariable);
SWC не может статически анализировать такой импорт, поэтому:
require без изменений
Это важно для сохранения динамической природы CommonJS.
В CommonJS порядок исполнения критичен:
const a = require("./a");
const b = require("./b");
SWC сохраняет строгий порядок:
require остаются на месте вызова
exports могут быть перемещены только если это не
меняет семантику
Это особенно важно для модулей с побочными эффектами.
В CommonJS существуют две основные сущности:
module.exports — итоговый экспорт модуля
exports — ссылка на module.exports
SWC учитывает следующее правило:
exports.a = 1;
module.exports = {};
После присваивания module.exports ссылка
exports больше не синхронизируется. Поэтому транспиляция
избегает смешивания стилей, либо явно переопределяет экспорт:
module.exports = { a: 1 };
Поведение трансформации управляется через параметры компиляции:
commonjs
Типичная конфигурационная модель:
{
"module": {
"type": "commonjs"
}
}
Дополнительно могут использоваться опции управления интеропом и генерацией вспомогательных функций, влияющие на:
__esModule
SWC стремится минимизировать накладные расходы:
require
В production-режиме при агрессивной оптимизации возможны:
Несмотря на высокую точность, остаются ограничения:
SWC не изменяет фундаментальную модель CommonJS, а лишь обеспечивает совместимость синтаксиса ESM с ней.