В системах сборки и трансформации кода SWC особое внимание уделяется
совместимости модулей CommonJS и ES Modules. Именно в этом слое
появляются настройки interopRequireDefault и
interopRequireWildcard, которые определяют, каким образом
транспайлер формирует обвязку импортов при переходе между разными
системами модулей.
При компиляции ESModule-кода в CommonJS возникает фундаментальная проблема различий:
module.exports и require()
export default, export
const, import * as
Несовпадение этих моделей требует промежуточного слоя — interop-обёрток, которые нормализуют структуру импортируемых объектов.
SWC решает это через две ключевые стратегии:
interopRequireDefault — управление доступом к
default-экспорту
interopRequireWildcard — управление импортом всего модуля
как пространства имён
interopRequireDefault определяет, будет ли SWC
автоматически оборачивать CommonJS-модуль в объект вида:
{
default: exports
}
Это поведение критично для корректной работы ESModule-импорта:
import foo from "foo";
При активном interopRequireDefault:
const foo = require("foo");
трансформируется в:
const _foo = require("foo");
const foo = _foo && _foo.__esModule ? _foo.default : _foo;
__esModule: true, берётся
.default
Если interopRequireDefault: false, SWC делает более
прямолинейную трансформацию:
const foo = require("foo");
Без дополнительной проверки и без доступа к .default.
Это может приводить к несовместимости с ESM-библиотеками, ожидающими
default-поведение.
interopRequireDefault влияет на:
export default
interopRequireWildcard определяет, как SWC обрабатывает:
import * as mod from "mod";
или:
const mod = require("mod");
в контексте ESM-CJS совместимости.
При включённой опции SWC создаёт полноценный объект-обёртку:
const _mod = require("mod");
const mod = _mod && _mod.__esModule
? _mod
: Object.defineProperties({}, {
...Object.getOwnPropertyNames(_mod).reduce((acc, key) => {
if (key !== "default") {
acc[key] = {
enumerable: true,
get: () => _mod[key]
};
}
return acc;
}, {}),
default: {
enumerable: true,
value: _mod
}
});
default
При interopRequireWildcard: false результат упрощается:
const mod = require("mod");
Без нормализации структуры и без добавления default.
Это быстрее, но менее совместимо с ESM-семантикой.
Обе опции часто работают совместно и определяют разные аспекты одной задачи:
| Сценарий импорта | interopRequireDefault | interopRequireWildcard |
|---|---|---|
import x from
|
критически важен | не используется |
import * as x
|
косвенно влияет | ключевой механизм |
| CommonJS require | влияет на обёртку | влияет на структуру |
Настройки задаются в .swcrc:
{
"module": {
"type": "commonjs",
"interop": "auto"
}
}
В SWC поведение interop часто агрегируется через режимы:
“none” — без interop-обёрток
“auto” — автоматическое определение необходимости
“esModule” — принудительная ESM-совместимость
В более низкоуровневых конфигурациях поведение может уточняться:
{
"jsc": {
"transform": {
"legacyDecorator": false
}
},
"module": {
"type": "commonjs"
}
}
Хотя напрямую флаги interopRequireDefault и
interopRequireWildcard обычно не выставляются вручную в
SWC-конфигурации как в Babel, их логика присутствует внутри генерации
кода при выборе режима module.type.
Обе настройки опираются на маркер:
__esModule: true
Он добавляется SWC при транспиляции ESM-модулей в CommonJS:
exports.__esModule = true;
exports.default = something;
.default
import express from "express";
Без interop:
express может оказаться объектом модуля
express.default отсутствует
С interop:
const lib = require("esm-lib");
С interopRequireWildcard:
default
Без него:
При цепочке:
возникает риск двойной обёртки, который решается проверкой
__esModule.
__esModule
В крупных приложениях выбор режима interop влияет на:
require
Interop-механизмы тесно связаны с:
import → require
При отключении interop некоторые из этих слоёв становятся избыточными и удаляются из выходного кода.
require-interop
interopRequireDefault
interopRequireWildcard при import * as
React