Поле sideEffects в package.json

В процессе сборки модулей ключевую роль играет устранение неиспользуемого кода (tree-shaking). Для корректной работы этого механизма сборщик должен понимать, какие модули при импорте выполняют побочные эффекты, а какие являются «чистыми» и могут быть безопасно удалены при отсутствии использования экспортов.

Поле sideEffects в package.json служит декларацией поведения пакета относительно побочных эффектов. Оно используется как подсказка для сборщиков, включая esbuild, позволяя оптимизировать граф зависимостей и безопасно удалять неиспользуемые части кода.


Понятие побочных эффектов в JavaScript-модулях

Модуль считается имеющим побочные эффекты, если при его импорте происходит что-то, выходящее за рамки экспорта значений:

  • изменение глобального состояния
  • регистрация обработчиков событий
  • полифиллы
  • изменение прототипов
  • выполнение кода вне функций
// module.js
window.APP_VERSION = "1.0.0";

export const value = 42;

Даже если value не используется, сам факт импорта изменяет глобальное окружение.

Противоположный случай:

// pure.js
export function sum(a, b) {
  return a + b;
}

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


Синтаксис поля sideEffects в package.json

Поле может принимать два основных вида значений:

Булево значение

{
  "sideEffects": false
}

Значение false означает, что все файлы пакета не содержат побочных эффектов. Это самый агрессивный режим оптимизации.

Массив файлов

{
  "sideEffects": [
    "*.css",
    "./src/polyfills.js"
  ]
}

В этом случае:

  • указанные файлы считаются имеющими побочные эффекты
  • остальные могут быть безопасно вырезаны при tree-shaking

Роль sideEffects в esbuild

esbuild выполняет быстрый анализ модулей и опирается на sideEffects для принятия решений о включении или исключении файлов.

Основной принцип:

  • если модуль не используется и помечен как «без побочных эффектов», он удаляется из графа
  • если модуль потенциально имеет побочные эффекты, он сохраняется даже при отсутствии экспортов

Поведение esbuild при sideEffects: false

При наличии:

{
  "sideEffects": false
}

esbuild считает:

  • каждый файл пакета безопасным для удаления
  • импорт без использования экспортов может быть полностью исключён

Пример:

import "lib/register";

import { add } from "lib/math";

Если register не влияет на итоговый результат сборки, esbuild может удалить этот импорт.


Частичные побочные эффекты через массив

Массив позволяет точечно контролировать поведение:

{
  "sideEffects": [
    "**/*.css",
    "src/init.js"
  ]
}

Поведение:

  • CSS-файлы всегда сохраняются (они изменяют DOM при загрузке через плагины)
  • init.js всегда выполняется
  • остальные модули считаются чистыми

Влияние на tree-shaking в esbuild

Tree-shaking в esbuild строится на нескольких уровнях:

  1. Анализ ESM-экспорта
  2. Проверка фактического использования символов
  3. Учет sideEffects

Если одновременно выполняются условия:

  • экспорт не используется
  • модуль не помечен как side-effectful

то модуль удаляется из итогового бандла.


Взаимодействие с ESM и CommonJS

esbuild более эффективно оптимизирует ESM-модули, поскольку их структура статически анализируема.

ESM:

export const a = 1;
export const b = 2;

Легко удаляются неиспользуемые экспорты.

CommonJS:

module.exports = {
  a: 1,
  b: 2
};

Tree-shaking ограничен, но sideEffects всё равно влияет на включение самого файла.


CSS и sideEffects

CSS часто рассматривается как побочный эффект, поскольку его импорт изменяет глобальное состояние страницы.

{
  "sideEffects": [
    "*.css"
  ]
}
import "./styles.css";

Даже если CSS не связан с JS-логикой, его удаление недопустимо.


Типичные ошибки при использовании sideEffects

Ошибка: ложное указание false

{
  "sideEffects": false
}

при наличии:

import "./polyfill";

Если polyfill изменяет глобальные объекты, его удаление приведёт к нестабильному поведению приложения.


Ошибка: слишком широкие маски

{
  "sideEffects": [
    "src/**"
  ]
}

В этом случае оптимизация почти полностью отключается, так как каждый файл считается потенциально опасным.


Поведение при отсутствии поля sideEffects

Если поле не указано:

  • esbuild не получает явной информации о побочных эффектах
  • применяется осторожная стратегия
  • часть возможностей tree-shaking может быть ограничена

Взаимодействие с экспортами и побочными эффектами

Важно различать:

  • использование экспорта
  • выполнение кода при импорте
// logger.js
console.log("module loaded");

export function log() {}

Даже если log не используется, сообщение может быть выведено при импорте, что делает модуль side-effectful.


Оптимизация структуры пакета

Корректная настройка sideEffects позволяет esbuild:

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

При этом важна точность декларации, так как сборщик не анализирует динамическое поведение глубже статических признаков.


Влияние на плагины и трансформации

Плагины esbuild, работающие с импортами CSS, SVG или других ресурсов, также учитывают sideEffects:

  • CSS loader сохраняет файлы с побочными эффектами
  • inline-ресурсы могут быть удалены при отсутствии ссылок
  • генерация виртуальных модулей зависит от флага sideEffects

Совместимость с экосистемой npm

Многие npm-пакеты используют:

{
  "sideEffects": false
}

как сигнал для современных сборщиков:

  • webpack
  • esbuild
  • rollup

Однако поведение может отличаться в деталях, особенно при смешанных ESM/CJS пакетах.


Логика принятия решения esbuild

Упрощённая модель:

  1. Построить граф импортов
  2. Определить используемые символы
  3. Проверить sideEffects
  4. Удалить недостижимые узлы
  5. Оставить только необходимые модули

Влияние на производительность сборки

Уменьшение количества учитываемых модулей приводит к:

  • снижению времени анализа зависимостей
  • сокращению объёма промежуточного AST
  • уменьшению выходного кода

Особенно заметно в монорепозиториях и UI-фреймворках с большим количеством утилитарных модулей.