SWC рассматривает import() как синтаксическую конструкцию
языка, которая может быть либо сохранена как есть, либо преобразована в
зависимость от целевого формата модулей и последующей цепочки сборки.
Ключевой момент: SWC сам по себе не выполняет полноценный
бандлинг по умолчанию, а трансформирует AST-код. Поэтому
поведение dynamic import определяется сочетанием:
module.type (ESM / CommonJS / UMD)
target)
При module.type: “es6” или сохранении ESM-выхода SWC
оставляет import() без изменений.
const mod = await import(&
Особенности поведения
-
import() остаётся нативным ESM выражением
-
код разделяется на чанки только на уровне бандлера
-
поддерживается top-level await в связке с ESM (если целевая среда
позволяет)
-
specifier должен оставаться валидным ESM-выражением
Динамические выражения
const mod = await import(`./locale/${lang}.js`);
SWC не запрещает такие конструкции, но:
-
анализ зависимостей невозможен на этапе трансформации
-
код-сплиттинг зависит от bundler’а
-
при отсутствии bundler’а результат будет выполняться в runtime без
оптимизации
CommonJS-формат и трансформация dynamic import
При module.type: “commonjs” SWC должен обеспечить
совместимость, поскольку в CJS отсутствует нативный
import().
Базовая трансформация
const mod = await import('./module.js');
может быть преобразовано в runtime-обёртку через require()
или helper-функцию.
Типичная модель поведения:
-
import() эмулируется через асинхронную обёртку
-
возвращается Promise
-
модуль загружается через
require (если нет code-splitting
системы)
Ограничения CommonJS-режима
-
нет стандартного chunk splitting без bundler-а
-
циклические зависимости могут вести себя иначе, чем в ESM
-
import() теряет семантику lazy-loading на уровне файлов
Динамический specifier
await import(getPath());
В CommonJS-режиме SWC не может предсказать зависимость, поэтому:
-
вызов остаётся runtime-динамическим
-
оптимизации невозможны
-
сборщик (если есть) обязан интерпретировать выражение отдельно
UMD и legacy-форматы
При module.type: “umd” поведение становится максимально
ограниченным.
Основная модель
-
import() либо эмулируется через fallback
-
либо оставляется как runtime-ошибка в старых окружениях
-
чаще всего требуется внешний runtime-полифилл
Практическое следствие
UMD-сборки редко поддерживают полноценный dynamic import без:
-
bundler runtime
-
системного loader-а (например SystemJS)
Поведение при отсутствии module трансформации
Если SWC используется только как транспайлер JSX/TS без изменения
модулей:
{
"jsc": {
"parser": {
"syntax": "typescript"
}
}
}
и при этом module не задан, тогда:
-
import() сохраняется как есть
-
TypeScript/JS semantics полностью переходят в runtime
-
ответственность за поддержку ложится на окружение выполнения
Влияние bundler-слоя на dynamic import
SWC часто используется вместе с системами, которые добавляют собственный
граф зависимостей.
Webpack-подобное поведение
-
import() превращается в split point
-
каждый динамический импорт становится отдельным чанком
-
строковые литералы анализируются статически
import('./a.js'); // chunk A
import('./b.js'); // chunk B
Ограничение динамики
import(`./pages/${name}.js`);
-
создаётся контекстный chunk (context module)
-
все потенциальные файлы включаются в сборку
-
SWC не управляет этим, но сохраняет выражение
SWC и interop между ESM и CommonJS
Dynamic import часто становится мостом между форматами.
ESM → CJS
const cjs = await import('./cjs-module.js');
Результат:
-
CJS модуль оборачивается в ESM-совместимый объект
-
экспорт попадает в
default или через named interop (в
зависимости от runtime)
CJS → ESM
При require() в CommonJS:
-
SWC не может создать полноценный ESM live binding
-
dynamic import используется как способ асинхронного доступа к
ESM-ресурсам
Оптимизация и tree-shaking взаимодействие
Dynamic import влияет на граф зависимостей принципиально иначе, чем
статический.
Статический import
import { a } from './mod.js';
-
участвует в tree-shaking
-
анализируется на этапе сборки
Dynamic import
await import('./mod.js');
-
исключается из статического графа
-
становится отдельной точкой входа
-
tree-shaking применяется только внутри загружаемого чанка
Async context и top-level await
В ESM-режиме SWC сохраняет совместимость с async loading:
const data = await import('./data.js');
Если целевая среда поддерживает top-level await:
-
модуль может блокироваться до загрузки зависимостей
-
порядок выполнения становится асинхронным графом
В CommonJS:
-
top-level await недоступен
-
SWC оборачивает код в async function (если включены соответствующие
трансформации)
Edge cases dynamic import
- Нестроковые выражения
import(1 + 2);
-
SWC не валидирует семантику
-
ошибка уходит в runtime
- Conditional import
const mod = await (flag ? import('./a.js') : import('./b.js'));
-
SWC сохраняет структуру
-
выбор модуля происходит полностью в runtime
- Import of JSON / assets
await import('./data.json');
Поведение зависит от bundler-а:
-
SWC не интерпретирует тип ресурса
-
loader решает, как преобразовать JSON (ESM export default или raw
object)
Итоговые различия поведения по форматам
ESM
-
dynamic import нативный
-
оптимизации зависят от bundler-а
-
поддерживается lazy loading и code splitting
CommonJS
-
import() эмулируется
-
отсутствует полноценный module graph
-
возможна потеря lazy semantics
UMD / legacy
-
ограниченная или отсутствующая поддержка
-
требуется внешний runtime loader
Роль SWC в общей цепочке
SWC в контексте dynamic import выполняет строго определённую функцию:
-
сохраняет или трансформирует синтаксис import()
-
адаптирует его под выбранный модульный формат
-
не управляет полноценным code splitting без внешнего bundler-а
-
обеспечивает совместимость между ESM и CommonJS через минимальные
обёртки
Поведение dynamic import всегда определяется не только SWC, но и
финальной системой выполнения модулей, в которой он используется.