Настройки treeshake.propertyReadSideEffects

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

Чтение свойства в JavaScript формально может быть как «чистой» операцией (obj.value), так и операцией с побочными эффектами (get value() { ... }, Proxy, вычисляемые геттеры). Именно эту неоднозначность и регулирует параметр treeshake.propertyReadSideEffects.


Смысл treeshake.propertyReadSideEffects

Параметр treeshake.propertyReadSideEffects управляет тем, считает ли Rollup чтение свойства объекта потенциально имеющим побочные эффекты.

Базовая идея:

  • при значении true Rollup действует консервативно;
  • при значении false Rollup допускает более агрессивное удаление кода, считая чтение свойств безопасным.

По сути, речь идет о допущении:

можно ли считать obj.prop “чистой” операцией, которая не влияет на выполнение программы.


Поведение по умолчанию (true)

В стандартной конфигурации:

export default {
  treeshake: {
    propertyReadSideEffects: true
  }
}

Rollup исходит из того, что:

  • доступ к свойству может запускать геттер;
  • объект может быть Proxy;
  • значение свойства может быть вычисляемым;
  • побочные эффекты могут быть скрыты внутри getter-а.

Следствие этого подхода

Даже если код выглядит безопасным:

const value = obj.something;

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

  • импортирован из другого модуля;
  • имеет неизвестную структуру;
  • экспортируется как namespace.

Поведение при false

При отключении:

export default {
  treeshake: {
    propertyReadSideEffects: false
  }
}

Rollup начинает считать, что:

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

Результат

Улучшается степень сжатия и сокращения кода, но увеличивается риск некорректного поведения в случаях, когда:

  • используются getter-ы с логикой;
  • применяются Proxy с trap get;
  • библиотеки используют ленивые вычисления через свойства.

Геттеры и скрытые побочные эффекты

Наиболее проблемная зона — свойства с accessor-логикой:

export const config = {
  get token() {
    console.log('access');
    return computeToken();
  }
};

При обращении:

config.token;

происходит выполнение кода.

Влияние настройки

  • propertyReadSideEffects: true — Rollup сохраняет осторожность и может не удалять связанные участки.
  • propertyReadSideEffects: false — Rollup предполагает, что это чистое чтение, что может привести к удалению кода, влияющего на поведение.

Proxy и динамическое поведение

Proxy усиливают неопределенность:

const obj = new Proxy({}, {
  get(target, key) {
    console.log('access', key);
    return 123;
  }
});

Любое чтение:

obj.x;

может иметь эффект.

При false Rollup игнорирует этот класс поведения на уровне анализа, полагаясь на статическую эвристику. При true сохраняет более осторожную модель.


Влияние на namespace-импорты

Особенно важная область — import * as ns from 'module'.

Namespace-объекты в ESM являются прокси-подобными структурами, где каждое свойство связано с экспортом модуля.

import * as ns from './mod.js';

ns.value;

При включенной осторожной модели Rollup считает такие чтения потенциально значимыми для side effects, так как:

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

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


Влияние на tree-shaking граф

propertyReadSideEffects влияет на то, как Rollup помечает узлы графа:

При true

  • чтение свойства может «подтянуть» весь объект;
  • зависимости становятся более связными;
  • уменьшается количество удаляемого кода.

При false

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

Взаимодействие с другими настройками treeshake

moduleSideEffects

Если модуль помечен как имеющий побочные эффекты:

{
  "sideEffects": true
}

то даже при propertyReadSideEffects: false Rollup может сохранить модуль целиком.

pure annotations

Комментарии вида:

/*#__PURE__*/ factory()

помогают Rollup понять, что результат вызова можно удалять. Но для свойств такой уровень аннотаций не применяется напрямую, поэтому роль propertyReadSideEffects становится критичнее.


Практические сценарии

1. Библиотечный код с чистыми объектами

export const math = {
  add(a, b) {
    return a + b;
  }
};

Здесь propertyReadSideEffects: false безопасен и улучшает tree-shaking.


2. Конфигурационные объекты с геттерами

export const config = {
  get api() {
    return loadApi();
  }
};

Здесь отключение может привести к пропуску важного кода.


3. Barrel exports

export { a } from './a.js';
export { b } from './b.js';

При активной агрессивной оптимизации Rollup может удалять неиспользуемые реэкспорты более эффективно при false.


Типичные ошибки конфигурации

Ошибка 1: глобальное отключение без анализа кода

Установка:

treeshake: {
  propertyReadSideEffects: false
}

в больших проектах без контроля качества зависимостей может привести к:

  • исчезновению инициализаций через getter-ы;
  • нарушению работы библиотек с Proxy;
  • некорректной ленивой загрузке.

Ошибка 2: ожидание ускорения без последствий

Хотя уменьшение conservative checks ускоряет tree-shaking, основное влияние — не на скорость, а на корректность результата сборки.


Ошибка 3: игнорирование сторонних зависимостей

Некоторые npm-библиотеки используют:

  • динамические свойства;
  • ленивые конфигурации;
  • side-effectful getters.

Для них false может быть небезопасен.


Баланс между безопасностью и агрессивной оптимизацией

treeshake.propertyReadSideEffects фактически задает уровень доверия к структуре объектов в кодовой базе.

  • высокий уровень доверия (false) — максимальная оптимизация;
  • низкий уровень доверия (true) — стабильность и предсказуемость.

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