Аннотация /*#__PURE__*/

Аннотация /*#__PURE__*/ представляет собой специальный комментарий, используемый в JavaScript-коде для обозначения чистых выражений, не имеющих побочных эффектов. В контексте Rollup и других современных бандлеров она играет ключевую роль в процессе tree-shaking, позволяя безопасно удалять неиспользуемый код при сборке.

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


Механизм tree-shaking и роль аннотаций

Tree-shaking в Rollup основан на анализе ES-модулей и построении графа зависимостей. Каждый экспорт рассматривается как потенциально удаляемый элемент, если он не достигается из точки входа.

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

В таких случаях используется аннотация:

const value = /*#__PURE__*/ createExpensiveObject();

Она сообщает бандлеру, что:

  • вызов createExpensiveObject() не имеет побочных эффектов
  • результат можно безопасно удалить, если value не используется
  • сам вызов не изменяет состояние приложения

Синтаксическая форма и размещение

Аннотация всегда размещается непосредственно перед выражением, которое считается «чистым»:

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

Допустимые формы применения:

Вызов функции

const result = /*#__PURE__*/ compute();

Конструктор

const instance = /*#__PURE__*/ new Service();

Вложенные выражения

const value = /*#__PURE__*/ wrap( /*#__PURE__*/ create());

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


Поведение Rollup при обработке PURE-аннотаций

Rollup сам по себе не «исполняет» аннотации, но учитывает их при:

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

Аннотация влияет на решение: является ли выражение side-effectful или нет.

Если выражение помечено как pure, Rollup может:

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

Связь с ES-модулями и статическим анализом

ESM является ключевым условием эффективного tree-shaking. Rollup анализирует импорт-экспорт как статический граф:

import { init } from './module.js';

init();

Если init помечен как неиспользуемый или удаляемый, но внутри него есть вызовы, аннотация /*#__PURE__*/ может помочь определить безопасность удаления.

В отличие от CommonJS, где динамические require затрудняют анализ, ES-модули позволяют точно отслеживать зависимости.


Типы выражений, где аннотация критична

1. Фабричные функции

export const store = /*#__PURE__*/ createStore();

Без аннотации Rollup предполагает, что createStore() может менять глобальное состояние.

2. React-подобные компоненты

export const App = /*#__PURE__*/ createComponent({
  name: 'App'
});

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

3. Инициализация модулей

export const config = /*#__PURE__*/ loadConfig();

Если loadConfig() читает файлы или обращается к окружению, аннотация становится некорректной.


Ограничения и ложные предположения

Аннотация /*#__PURE__*/ не выполняет никакой проверки корректности. Она является исключительно подсказкой для бандлера.

Ключевые ограничения:

  • не проверяет фактическое наличие побочных эффектов
  • может привести к ошибкам при неверном использовании
  • игнорируется, если код не проходит через совместимый minifier или bundler
  • не заменяет анализ package.json поля sideEffects

Взаимодействие с sideEffects в package.json

В экосистеме Rollup и связанных инструментов используется два уровня оптимизации:

package.json

{
  "sideEffects": false
}

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

PURE-аннотация

/*#__PURE__*/ fn();

Работает на уровне конкретного выражения.

Различие:

  • sideEffects — декларация уровня модуля или пакета
  • /*#__PURE__*/ — точечная аннотация для AST-узла

Они могут комбинироваться, усиливая tree-shaking.


Поведение при минификации

Хотя аннотация изначально связана с Rollup, она активно используется минификаторами, такими как Terser.

При наличии PURE-комментария минификатор может:

  • удалить вызов функции
  • удалить всю строку
  • оптимизировать цепочку вызовов

Пример трансформации:

Исходный код:

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

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

// удалено полностью, если x не используется

Неправильное использование

Ошибочное применение аннотации приводит к логическим багам.

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

const el = /*#__PURE__*/ document.createElement('div');
document.body.appendChild(el);

Здесь createElement кажется «чистым», но реальный эффект проявляется позже через appendChild. Удаление первой строки изменяет поведение программы.


Влияние на классы и инстанцирование

Аннотация может применяться к new выражениям:

const service = /*#__PURE__*/ new ApiService();

Однако корректность зависит от конструктора. Если конструктор:

  • подписывается на события
  • модифицирует глобальное состояние
  • выполняет I/O

то аннотация становится некорректной.


Композиция аннотаций в цепочках вызовов

В сложных выражениях аннотация применяется к каждому узлу отдельно:

const value = /*#__PURE__*/ wrap( /*#__PURE__*/ transform(input));

Rollup анализирует:

  • внешний вызов wrap
  • внутренний вызов transform

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


Роль в современных сборках

Аннотация /*#__PURE__*/ стала стандартом де-факто в инструментах сборки Jav * aScript:

  • Rollup использует её для tree-shaking
  • Terser — для dead code elimination
  • Babel — добавляет автоматически при транспиляции (через @babel/plugin-transform-react-pure-annotations)
  • Webpack — учитывает при оптимизации модулей

Автоматическая генерация аннотаций

Современные пайплайны часто вставляют PURE-комментарии автоматически:

  • при транспиляции JSX
  • при компиляции TypeScript
  • при сборке библиотек UI-компонентов

Пример:

React.createElement(App, null);

Может быть преобразован в:

/*#__PURE__*/ React.createElement(App, null);

Статический характер аннотации

Аннотация полностью статична и не может:

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

Она существует исключительно на уровне исходного текста и AST.


Связь с архитектурой модулей Rollup

В Rollup граф модулей строится как ориентированный ациклический граф. PURE-аннотация влияет на поведение узлов в этом графе:

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

Это позволяет уменьшать размер бандла без изменения логики приложения при корректном использовании аннотаций.


Практика в библиотечном коде

В библиотеках PURE-аннотация применяется особенно активно:

export const utils = /*#__PURE__*/ createUtils();
export const config = /*#__PURE__*/ loadConfig();
export const api = /*#__PURE__*/ buildApiClient();

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


Итоговая роль в оптимизации

Аннотация /*#__PURE__*/ является точечным механизмом управления поведением статического анализатора, позволяя уточнять границы побочных эффектов и усиливать эффективность tree-shaking в Rollup-сборках, особенно в условиях сложных графов зависимостей и библиотечных архитектур.