Поле sideEffects в package.json

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

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

  • изменение глобальных объектов (window, globalThis)
  • регистрация полифилов
  • добавление CSS через JS-инъекции
  • выполнение инициализирующего кода
  • регистрация плагинов или подписок

Пример модуля с побочным эффектом:

// polyfill.js
import './setupGlobals.js';

window.myPolyfillEnabled = true;

Даже если модуль не экспортирует ничего, его импорт изменяет состояние окружения, поэтому удаление такого импорта может сломать приложение.

Значение sideEffects в package.json

Поле sideEffects задаёт сборщику правило: можно ли считать файлы пакета «чистыми» (pure) с точки зрения удаления неиспользуемого кода.

Форматы значения:

Полное отсутствие побочных эффектов

{
  "name": "my-library",
  "sideEffects": false
}

Это означает:

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

Наличие побочных эффектов во всех файлах

{
  "name": "my-library",
  "sideEffects": true
}

Это означает:

  • каждый импорт потенциально имеет побочные эффекты
  • tree-shaking ограничен
  • удаление модулей без анализа запрещено

Частичное указание файлов

{
  "name": "my-library",
  "sideEffects": [
    "*.css",
    "./src/polyfills.js"
  ]
}

Здесь:

  • CSS-файлы всегда считаются имеющими побочные эффекты
  • конкретный файл polyfills.js сохраняется
  • остальные JS-модули считаются «чистыми» и могут быть оптимизированы

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

Tree-shaking основан на анализе ES-модулей, где импорт и экспорт статически определены. Однако одного анализа недостаточно: сборщик должен понимать, можно ли безопасно удалить импортируемый модуль, если его экспорт не используется.

Без sideEffects

При sideEffects: false сборщик делает предположение:

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

Пример:

import { helper } from 'utils';

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

С sideEffects: true

При включённых побочных эффектах:

  • даже неиспользуемый импорт может быть сохранён
  • сборщик не может безопасно удалить модуль
  • оптимизация становится консервативной

Поведение в Rollup

Rollup выполняет tree-shaking на основе статического анализа графа модулей ES6. В отличие от некоторых других сборщиков, Rollup не полагается исключительно на sideEffects, но может учитывать его через плагины резолвинга зависимостей.

Основные особенности:

  • Rollup анализирует экспорт/импорт напрямую
  • определяет «мертвый код» без выполнения
  • sideEffects используется как дополнительная подсказка при работе с npm-пакетами
  • особенно важно при использовании @rollup/plugin-node-resolve

При сборке зависимостей из node_modules поле sideEffects может повлиять на решение о том, можно ли безопасно выкинуть неиспользуемые импорты внутри пакета.

Пример влияния в реальном пакете

Рассмотрим библиотеку:

{
  "name": "ui-kit",
  "sideEffects": false,
  "module": "dist/index.js"
}

И файл:

// button.js
export function Button() {
  return document.createElement('button');
}

// index.js
export { Button } from './button.js';
export { Modal } from './modal.js';

Если пользователь импортирует только Button:

import { Button } from 'ui-kit';

Rollup может построить граф:

  • index.js используется
  • modal.js не используется
  • button.js используется

При наличии sideEffects: false модуль modal.js может быть полностью исключён из бандла.

Гранулярная настройка sideEffects

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

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

Это позволяет:

  • сохранять стили без риска удаления
  • сохранять инициализационные скрипты
  • оптимизировать основную JS-логику

CSS и побочные эффекты

CSS-импорты через JS являются типичным примером побочных эффектов:

import './styles.css';

Удаление такого импорта приведёт к отсутствию стилей в приложении, поэтому:

  • CSS почти всегда помечается как side effect
  • даже при sideEffects: false используется исключение *.css

Ошибки конфигурации sideEffects

Неправильная настройка приводит к критическим проблемам:

Ложное указание false

{
  "sideEffects": false
}

Если в пакете есть:

import './init.js';

где init.js регистрирует глобальные обработчики, сборщик может удалить этот импорт, что приведёт к неработоспособности функционала.

Слишком широкое указание true

{
  "sideEffects": true
}

Результат:

  • tree-shaking почти не работает
  • увеличивается размер бандла
  • теряется смысл ES-модульной структуры

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

sideEffects наиболее эффективно работает с ES-модулями:

  • ESM позволяет статический анализ
  • CommonJS снижает точность tree-shaking
  • Rollup может частично анализировать CJS, но с ограничениями

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

Практическая модель принятия решения сборщиком

При анализе модуля сборщик учитывает:

  1. Используются ли экспортируемые символы
  2. Есть ли побочные эффекты в модуле
  3. Что указано в sideEffects
  4. Можно ли безопасно удалить импорт без изменения поведения

При sideEffects: false вероятность удаления модуля значительно выше, при условии отсутствия прямого использования экспортов.

Связь с оптимизацией бандла

Правильная настройка sideEffects напрямую влияет на:

  • размер итогового bundle
  • количество загружаемого кода
  • скорость загрузки приложения
  • эффективность кеширования

Особенно заметно это в больших UI-библиотеках и design system-пакетах, где десятки компонентов могут быть импортированы, но используется только часть.

Типичный паттерн для библиотек

Базовая структура package.json в библиотеке:

{
  "name": "component-library",
  "version": "1.0.0",
  "module": "dist/index.js",
  "sideEffects": [
    "*.css"
  ]
}

Такой подход:

  • позволяет удалять неиспользуемые компоненты
  • сохраняет стили
  • сохраняет предсказуемость инициализации
  • обеспечивает совместимость с Rollup и другими сборщиками

Итоговая роль в экосистеме сборки

sideEffects выступает метаданными уровня пакета, которые дополняют статический анализ модулей. В связке с Rollup он усиливает точность tree-shaking при работе с внешними зависимостями, не заменяя анализ, а уточняя границы безопасного удаления кода.