Экосистема JavaScript исторически разделена на две модульные системы:
CommonJS и ECMAScript Modules. CommonJS использует
require() и module.exports, тогда как ESM
опирается на import и export. Эти системы
несовместимы на уровне синтаксиса и имеют различную семантику загрузки и
экспорта.
При компиляции TypeScript или современного JavaScript в целевую среду
SWC вынужден решать задачу интероперабельности: как корректно
импортировать CommonJS-модуль как ESM и наоборот. Именно здесь
появляется механизм interop и опция noInterop.
noInterop в SWC
В конфигурации SWC параметр noInterop управляет генерацией
вспомогательного кода для совместимости модулей.
Типичная конфигурация:
{
"module": {
"type": "es6",
"noInterop": true
}
}
Смысл параметра:
noInterop: false — SWC добавляет слой совместимости между
CommonJS и ESM
noInterop: true — SWC отключает этот слой и сохраняет
«сырую» модульную семантику
Ключевой эффект заключается в том, будет ли SWC автоматически
подставлять обёртки для default import из CommonJS.
noInterop: false
При включённой интероперабельности SWC стремится сделать импорт CommonJS максимально похожим на ESM.
// cjs-module.js
module.exports = {
value: 42
};
import mod from "./cjs-module";
При noInterop: false SWC генерирует код, который фактически
преобразует CommonJS экспорт в объект с default:
const mod = require("./cjs-module");
const _mod = mod && mod.__esModule ? mod : { default: mod };
_mod.default;
import x from “cjs” работает как ожидается
default автоматически синтезируется
noInterop: true
При отключённой интероперабельности SWC перестаёт «угадывать» структуру CommonJS-модулей.
{
"module": {
"type": "es6",
"noInterop": true
}
}
import mod from "./cjs-module";
Теперь результат становится прямолинейным:
const mod = require("./cjs-module");
default
module.exports
module.exports = {
value: 10
};
noInterop: true
import mod from "./cjs";
mod.value; // работает
mod.default; // undefined
noInterop: false
import mod from "./cjs";
mod.default.value; // работает через обёртку
Разница критична: меняется точка доступа к данным.
| Сценарий | noInterop: false | noInterop: true |
|---|---|---|
| CommonJS default import |
обёртка default
|
прямой require()
|
| ESM import/export | без изменений | без изменений |
| Совместимость с Babel-style interop | высокая | низкая |
| Прозрачность структуры модулей | низкая | высокая |
| Риск runtime ошибок | ниже | выше при смешанных модулях |
Отключение interop-обёрток влияет на то, как сборщик анализирует зависимости.
noInterop: false
noInterop: true
require
default
Многие ошибки в JavaScript-проектах возникают из-за:
import lib from "lib";
lib.default(); // runtime error
При noInterop: true такой слой просто отсутствует, и ошибка
проявляется сразу на этапе доступа к свойствам.
Код начинает отражать реальную структуру пакета:
В экосистеме TypeScript часто используется параметр
esModuleInterop. SWC noInterop концептуально
связан, но работает на уровне трансформации, а не типов.
noInterop: false
esModuleInterop: true
noInterop: true
import * as mod from "./cjs";
noInterop
import express from "express";
express(); // может перестать работать
Если express — CommonJS модуль, структура экспорта может
требовать:
import * as express from "express";
Библиотеки, которые частично используют module.exports и
export default, начинают вести себя неоднозначно.
import lib from "lib";
lib.default.fn(); // ломается
noInterop
При использовании SWC вместе с bundlers (например, Webpack, Vite,
Rollup) noInterop влияет на раннюю стадию трансформации.
Это меняет:
В масштабных проектах влияние noInterop проявляется не
сразу, а через накопление различий:
На уровне архитектуры модулей это создаёт разделение между:
noInterop: false — слой совместимости, сглаживание различий
модульных систем
noInterop: true — прямое отображение исходной модульной
системы без синтетических преобразований
Разница определяется не только синтаксисом импорта, но и тем, как именно формируется объект модуля на этапе выполнения.