Поведение с dynamic import при разных форматах

Общая модель обработки dynamic import в SWC

SWC рассматривает import() как синтаксическую конструкцию языка, которая может быть либо сохранена как есть, либо преобразована в зависимость от целевого формата модулей и последующей цепочки сборки.

Ключевой момент: SWC сам по себе не выполняет полноценный бандлинг по умолчанию, а трансформирует AST-код. Поэтому поведение dynamic import определяется сочетанием:

  • module.type (ESM / CommonJS / UMD)
  • целевой среды (target)
  • наличия bundler-слоя (webpack, Turbopack, Vite, esbuild-совместимые пайплайны)
  • настроек трансформации

ESM-формат и native dynamic import

При 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

  1. Нестроковые выражения

import(1 + 2);
  • SWC не валидирует семантику
  • ошибка уходит в runtime

  1. Conditional import

const mod = await (flag ? import('./a.js') : import('./b.js'));
  • SWC сохраняет структуру
  • выбор модуля происходит полностью в runtime

  1. 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, но и финальной системой выполнения модулей, в которой он используется.