Поле isModule и его влияние на обработку

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

Ключевая идея заключается в том, что одинаковый синтаксис JavaScript может интерпретироваться по-разному в зависимости от контекста выполнения. SWC, как компилятор и трансформер, обязан строго учитывать этот контекст, поскольку он меняет семантику почти всех верхнеуровневых конструкций.


Логическая природа isModule

Флаг isModule определяет режим интерпретации исходного файла:

  • true — файл рассматривается как ECMAScript Module (ESM)
  • false — файл рассматривается как Script (обычный скрипт)

Это бинарное различие влияет на:

  • правила области видимости;
  • доступность синтаксиса import / export;
  • поведение this на верхнем уровне;
  • строгий режим выполнения;
  • порядок инициализации кода;
  • допустимость top-level await;
  • интерпретацию hoisting и eval.

SWC использует этот флаг на этапе AST-построения и далее распространяет его через все стадии трансформации.


Влияние на парсинг исходного кода

На этапе парсинга isModule определяет допустимый набор синтаксических конструкций.

Модули (isModule = true)

При включённом режиме модуля:

  • разрешены import и export;
  • весь файл автоматически считается строгим (strict mode);
  • запрещено использование некоторых legacy-конструкций в их старом поведении;
  • поддерживается top-level await (в современных конфигурациях SWC);
  • код анализируется как ESM-граф.

Пример:

import { readFile } from "fs";

export const value = await readFile("data.txt", "utf-8");

Скрипты (isModule = false)

В режиме скрипта:

  • import и export синтаксически недопустимы;
  • строгий режим не включается автоматически;
  • this на верхнем уровне указывает на глобальный объект;
  • код интерпретируется как единый глобальный контекст.

Пример:

var a = 1;

function test() {
  return this;
}

Влияние на область видимости

Одним из наиболее критичных эффектов isModule является изменение модели scope.

Модульная область видимости

В режиме isModule = true:

  • каждый файл имеет собственный модульный scope;
  • верхнеуровневые объявления не попадают в глобальную область;
  • переменные изолированы;
  • экспортируемые сущности формируют явный интерфейс модуля.
const secret = 42;

export const api = {
  getSecret() {
    return secret;
  }
};

Переменная secret недоступна извне, несмотря на то что она находится на верхнем уровне.

Скриптовая область видимости

При isModule = false:

  • верхнеуровневые var могут попадать в global scope;
  • поведение зависит от среды выполнения;
  • возможны конфликты имён между файлами;
  • порядок загрузки скриптов критичен.
var shared = 10;

В браузерной среде это может стать window.shared.


Влияние на strict mode

SWC использует isModule как триггер автоматического включения строгого режима.

Модуль всегда strict

При isModule = true код неявно интерпретируется как:

"use strict";

Это приводит к:

  • запрету неявного создания глобальных переменных;
  • изменению поведения this (undefined в функциях);
  • усиленной проверке присваиваний;
  • запрету дублирования параметров функций.

Скрипт может быть non-strict

При isModule = false строгий режим зависит от:

  • наличия “use strict”;
  • конфигурации трансформаций SWC;
  • обёрток bundler-а.

Обработка import/export

Одно из ключевых влияний isModule связано с поддержкой ESM-синтаксиса.

При isModule = true

SWC:

  • строит dependency graph;
  • анализирует import и export;
  • может выполнять tree-shaking;
  • преобразует модули в target format (CommonJS, AMD, IIFE).
import { a } from "./mod";
export const b = a + 1;

На этапе трансформации может стать:

const mod_1 = require("./mod");
exports.b = mod_1.a + 1;

При isModule = false

  • import/export запрещены;
  • нет построения модульного графа;
  • код обрабатывается как независимый скрипт;
  • любые попытки модульного синтаксиса приводят к ошибке парсинга.

Top-level await и асинхронный контекст

Флаг isModule критически важен для поддержки top-level await.

Модули

При isModule = true SWC допускает:

const data = await fetch("/api");

Это требует:

  • асинхронного оборачивания модуля при транспиляции в CJS;
  • генерации async wrapper function;
  • корректной обработки порядка выполнения.

Скрипты

При isModule = false:

  • top-level await запрещён;
  • await допустим только внутри async функций;
  • попытка использования приводит к синтаксической ошибке.

Поведение this на верхнем уровне

Различие между модулем и скриптом особенно заметно в значении this.

Модуль

console.log(this);

Результат: undefined

SWC сохраняет это поведение при генерации кода, не добавляя глобальных привязок.

Скрипт

console.log(this);

Результат зависит от среды:

  • в браузере — window;
  • в Node.js — global (или module wrapper).

SWC при транспиляции скриптов учитывает возможную обёртку CommonJS.


Влияние на hoisting и декларации

Хотя hoisting присутствует в обоих режимах, isModule меняет контекст его интерпретации.

В модулях

  • каждый файл изолирован;
  • var не выходит за пределы модуля;
  • function declarations остаются локальными.

В скриптах

  • возможна утечка в глобальный scope;
  • порядок загрузки влияет на доступность функций;
  • конфликты имен не предотвращаются системой модулей.

Влияние на трансформации SWC

isModule используется как условие для активации ряда трансформаций.

  1. Конвертация модулей

Если цель сборки — CommonJS:

  • isModule = true → активируется модульный трансформер;
  • выполняется преобразование import/export;
  • создаётся runtime-обвязка.

  1. Tree shaking

SWC может выполнять частичное удаление кода:

  • только при модульном режиме;
  • только при наличии ESM-графа;
  • зависит от анализа экспортов.

  1. Оборачивание кода

Для скриптов SWC может добавлять обёртки:

(function () {
  var a = 1;
})();

Для модулей обёртка строится иначе:

(function (exports, require, module, __filename, __dirname) {
  // module body
});

Влияние на interop с CommonJS

При транспиляции ESM → CJS значение isModule определяет стратегию интеропа:

  • создание __esModule флага;
  • генерация default export;
  • обработка именованных экспортов;
  • эмуляция live bindings.

Пример результата:

Object.defineProperty(exports, "__esModule", { value: true });
exports.default = main;

Ошибки и диагностические различия

SWC использует isModule для выбора набора синтаксических ошибок.

В модуле

  • запрещён with;
  • строгая проверка import/export;
  • обязательная валидность ESM-графа.

В скрипте

  • ошибки возникают только при несовместимом синтаксисе;
  • менее строгая проверка контекста модулей.

Связь с bundler-экосистемой

В реальных пайплайнах значение isModule редко определяется вручную. Оно зависит от:

  • расширения файла (.mjs, .cjs, .js);
  • package.json (type: module);
  • настроек bundler (Next.js, Vite, Webpack);
  • явной конфигурации SWC.

Неправильная установка isModule приводит к:

  • некорректному парсингу import/export;
  • ошибкам выполнения;
  • нарушению tree-shaking;
  • неверной генерации wrapper-ов.

Итоговая семантическая роль флага

Флаг isModule в SWC не является вспомогательной метаинформацией. Он задаёт фундаментальный контекст интерпретации кода, от которого зависит:

  • синтаксический анализ;
  • построение AST;
  • выбор трансформаций;
  • стратегия генерации кода;
  • совместимость с runtime;
  • корректность модульной модели исполнения.

Любая стадия компиляции SWC опирается на это значение как на первичный источник семантического режима файла.