Удаление мёртвого кода

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

Под «мёртвым кодом» в контексте JavaScript подразумеваются:

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

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


Механизм удаления мёртвого кода в SWC

В SWC удаление мёртвого кода выполняется на этапе минификации и включает несколько взаимосвязанных проходов:

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

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


Анализ графа зависимостей

Удаление мёртвого кода невозможно без анализа графа зависимостей. В SWC этот процесс строится вокруг следующих шагов:

  1. определение точек входа (entry points);
  2. рекурсивное построение графа импортов;
  3. маркировка используемых экспортов;
  4. распространение информации об использовании по графу;
  5. исключение недостижимых узлов.

Особое значение имеет различие между:

  • сильными зависимостями (обязательные импорты);
  • слабым использованием (динамические import() и реэкспорты);
  • побочными эффектами модулей.

Если модуль не имеет побочных эффектов и его экспорт не используется, он может быть полностью исключён.


Работа с ES Modules

Наиболее эффективное удаление мёртвого кода в SWC достигается при использовании ES Modules (import / export), поскольку их статическая структура позволяет анализировать зависимости на этапе компиляции.

Пример:

export function used() {
  return 1;
}

export function unused() {
  return 2;
}

Если импортируется только used, функция unused удаляется на этапе минификации.

Ключевые факторы:

  • статический характер импортов;
  • отсутствие динамических изменений экспортов;
  • возможность точного tree-shaking.

Ограничения CommonJS

При использовании CommonJS (require, module.exports) эффективность удаления мёртвого кода в SWC значительно снижается.

Причины:

  • require() может вызываться динамически;
  • module.exports может изменяться в рантайме;
  • невозможно гарантировать неизменность экспортов;
  • побочные эффекты при импорте модулей.

Пример проблемного паттерна:

const lib = require("./lib");

if (false) {
  lib.heavyFunction();
}

Хотя условие явно ложное, статический анализ не всегда может безопасно удалить код без риска изменения поведения.


Побочные эффекты и их учёт

Удаление мёртвого кода в SWC строго зависит от анализа побочных эффектов (side effects).

Модуль считается «чистым», если:

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

Пример побочного эффекта:

console.log("module loaded");

export const value = 42;

Такой модуль не может быть полностью удалён даже при отсутствии использования value.

Для управления поведением используются:

  • аннотации чистоты;
  • анализ выражений;
  • конфигурация сборки.

Аннотации чистоты и /

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

Наиболее распространённый механизм:

/*#__PURE__*/ createInstance();

Это позволяет минификатору:

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

Пример:

const x = /*#__PURE__*/ factory();

Если x не используется, вызов factory() удаляется полностью.


Настройки минификации и DCE

Конфигурация оптимизаций в SWC осуществляется через minify-параметры:

{
  jsc: {
    minify: {
      compress: {
        unused: true,
        dead_code: true
      },
      mangle: true
    }
  }
}

Ключевые параметры:

  • compress.unused — удаление неиспользуемых переменных и функций;
  • compress.dead_code — устранение недостижимых блоков;
  • mangle — сокращение имён идентификаторов после анализа использования.

Дополнительно учитываются:

  • агрессивное упрощение выражений;
  • свёртка констант;
  • удаление пустых блоков.

Инлайнинг и свёртка выражений

Удаление мёртвого кода тесно связано с другими оптимизациями в SWC:

  • constant folding (свёртка констант);
  • constant propagation (распространение значений);
  • inlining функций.

Пример:

function add(a, b) {
  return a + b;
}

const result = add(2, 3);

После оптимизации:

const result = 5;

Если result не используется, весь фрагмент удаляется.


Tree shaking и взаимодействие с бандлерами

Удаление мёртвого кода в SWC часто работает совместно с tree shaking на уровне сборщика.

Разделение ответственности:

  • SWC выполняет локальные и модульные оптимизации;
  • бандлер (например, Webpack, Vite, Rollup) удаляет неиспользуемые модули в графе зависимостей.

Эффективность максимальна при соблюдении условий:

  • использование ES Modules;
  • отсутствие побочных эффектов;
  • корректная настройка “sideEffects”: false в package.json.

Практика оптимизации кода

Поведение DCE в SWC зависит от структуры кода.

Неоптимальный пример:

function debug() {
  console.log("debug");
}

debug();

Если функция вызывается только в условии, которое всегда ложное, оптимизатор может удалить вызов, но не всегда саму функцию при сложных зависимостях.

Оптимизированный вариант:

/*#__PURE__*/ (() => {
  console.log("debug");
})();

При отсутствии использования результата весь блок может быть удалён.


Условные конструкции и удаление блоков

Мёртвый код часто возникает внутри условных выражений:

if (process.env.NODE_ENV === "production") {
  console.log("dev only");
}

В SWC такие конструкции могут быть устранены при известном значении переменной окружения на этапе сборки.

После подстановки:

if (false) {
  console.log("dev only");
}

Далее блок полностью удаляется как недостижимый.


Работа с объектами и неиспользуемыми свойствами

Удаление мёртвого кода распространяется и на свойства объектов:

const config = {
  a: 1,
  b: 2,
  c: 3
};

console.log(config.a);

После анализа использования:

const config = {
  a: 1
};

Остальные свойства исключаются, если отсутствуют побочные эффекты и динамическое обращение.


Влияние динамического кода

В SWC сложность резко возрастает при использовании:

  • eval;
  • with;
  • динамических ключей объектов;
  • рефлексивных паттернов (obj[key] с неизвестным key).

В таких случаях оптимизатор вынужден сохранять код, поскольку статический анализ не гарантирует корректность удаления.

const key = getKey();
obj[key] = 10;

Удаление свойств становится невозможным без потери семантики.