Опция drop: удаление debugger и console

При сборке приложений нередко возникает необходимость автоматически удалять отладочный код из финального бандла. В процессе разработки активно используются инструкции debugger, а также различные вызовы console.log(), console.warn(), console.error() и других методов объекта console. Эти конструкции помогают анализировать работу приложения, однако в производственной сборке зачастую оказываются ненужными.

Для решения этой задачи в Esbuild предусмотрена опция drop, позволяющая удалять определённые виды инструкций ещё на этапе трансформации исходного кода.

Поддерживаются два значения:

  • debugger
  • console

Удаление операторов debugger

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

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

function calculate(a, b) {
  debugger
  return a + b
}

Настройка Esbuild:

await esbuild.build({
  entryPoints: ['src/index.js'],
  bundle: true,
  drop: ['debugger'],
  outfile: 'dist/app.js'
})

Результат после сборки:

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

Все инструкции debugger полностью удаляются из итогового кода.

Использование через CLI

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

esbuild src/index.js \
  --bundle \
  --drop:debugger \
  --outfile=dist/app.js

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

esbuild src/index.js \
  --bundle \
  --drop:debugger \
  --drop:console \
  --outfile=dist/app.js

Удаление вызовов console

Во время разработки журналирование помогает отслеживать состояние приложения.

Пример:

console.log('Application started')

function processData(data) {
  console.log('Processing', data)

  if (!data) {
    console.error('Data is missing')
    return
  }

  console.warn('Validation complete')
}

Настройка:

await esbuild.build({
  entryPoints: ['src/index.js'],
  bundle: true,
  drop: ['console'],
  outfile: 'dist/app.js'
})

После сборки:

function processData(data) {
  if (!data) {
    return
  }
}

Esbuild удаляет обращения к объекту console, что позволяет уменьшить размер итогового бандла и убрать лишний отладочный вывод.

Какие методы удаляются

При использовании значения console удаляются вызовы методов объекта console, включая наиболее распространённые:

console.log()
console.info()
console.warn()
console.error()
console.debug()
console.trace()

Пример:

console.log('Start')
console.info('Info')
console.warn('Warning')
console.error('Error')
console.debug('Debug')

После применения:

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

Одновременное удаление debugger и console

Наиболее распространённый сценарий для production-сборки — удаление сразу обоих типов отладочных конструкций.

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

console.log('Loading')

debugger

function start() {
  console.log('Started')
  debugger
}

Конфигурация:

await esbuild.build({
  entryPoints: ['src/index.js'],
  bundle: true,
  minify: true,
  drop: ['debugger', 'console'],
  outfile: 'dist/app.js'
})

Результат:

function start(){}

Весь отладочный код удаляется автоматически.

Взаимодействие с минификацией

Опция drop работает независимо от минификации.

Конфигурация без минификации:

await esbuild.build({
  bundle: true,
  drop: ['debugger']
})

Конфигурация с минификацией:

await esbuild.build({
  bundle: true,
  minify: true,
  drop: ['debugger']
})

В обоих случаях инструкции debugger будут удалены.

Минификация отвечает за сокращение и оптимизацию кода, тогда как drop отвечает за устранение конкретных конструкций.

Отличие от Dead Code Elimination

Иногда возникает путаница между опцией drop и механизмом удаления неиспользуемого кода.

Рассмотрим пример:

function logMessage() {
  console.log('message')
}

logMessage()

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

drop: ['console']

получим:

function logMessage() {
}

logMessage()

Функция остаётся существовать, поскольку её вызов всё ещё присутствует.

Механизм tree shaking и удаление мёртвого кода решают другую задачу — поиск и устранение неиспользуемых сущностей.

Особенности удаления console

Важно понимать, что Esbuild удаляет сами вызовы методов объекта console, а не только текст сообщений.

Пример:

console.log(expensiveCalculation())

После применения:

Вызов expensiveCalculation() также исчезнет вместе с инструкцией логирования.

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

Рассмотрим пример:

console.log(updateDatabase())

После удаления:

Функция updateDatabase() больше не будет вызываться.

По этой причине не рекомендуется помещать бизнес-логику внутрь аргументов методов console.

Корректный вариант:

const result = updateDatabase()

console.log(result)

После удаления логирования:

const result = updateDatabase()

Основная логика приложения сохраняется.

Использование в разных режимах сборки

Часто удаление отладочного кода применяется только для production-сборок.

Пример:

const production = process.env.NODE_ENV === 'production'

await esbuild.build({
  entryPoints: ['src/index.js'],
  bundle: true,
  drop: production
    ? ['debugger', 'console']
    : [],
  outfile: 'dist/app.js'
})

В режиме разработки:

console.log('Debug info')
debugger

сохраняются без изменений.

В производственной сборке такие конструкции автоматически исчезают.

Использование вместе с define

Нередко опция drop комбинируется с заменой констант через define.

Пример:

await esbuild.build({
  bundle: true,
  define: {
    __DEV__: 'false'
  },
  drop: ['debugger', 'console']
})

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

if (__DEV__) {
  console.log('Development mode')
}

После обработки:

if (false) {
}

Далее минификатор и механизмы оптимизации могут полностью удалить недостижимый участок кода.

Практические рекомендации

Production-сборки

Для большинства веб-приложений оправдано использование:

drop: ['debugger', 'console']

Это уменьшает объём бандла и исключает случайное попадание отладочных сообщений в рабочую среду.

Библиотеки

При разработке библиотек стоит внимательно оценивать необходимость удаления console.

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

console.warn(...)
console.error(...)

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

Серверные приложения

Для серверного JavaScript логирование часто является частью основной функциональности мониторинга.

Пример:

console.error('Database connection failed')

Удаление подобных вызовов может привести к потере важных диагностических данных. В серверных проектах опцию drop: ['console'] следует использовать особенно осторожно.

Типичные ошибки

Удаление рабочего кода внутри логирования

Нежелательный вариант:

console.log(sendAnalytics())

После сборки:

Вызов sendAnalytics() исчезнет.

Правильный вариант:

const analyticsResult = sendAnalytics()

console.log(analyticsResult)

Ожидание удаления произвольных функций

Опция:

drop: ['console']

не удаляет пользовательские функции логирования.

Пример:

logger.info('Message')

останется без изменений:

logger.info('Message')

Поскольку Esbuild удаляет только обращения к встроенному объекту console.

Ожидание удаления всего отладочного кода

Конструкция:

const debugData = collectDebugInfo()

console.log(debugData)

после обработки превращается в:

const debugData = collectDebugInfo()

Переменная и вызов функции могут остаться в коде, если используются механизмы, отличные от удаления вызовов console. Для полного устранения подобных участков обычно применяются дополнительные техники: условная компиляция через define, tree shaking и минификация.

Сводка возможностей drop

Значение Что удаляется
debugger Все инструкции debugger
console Все вызовы методов объекта console
['debugger', 'console'] Оба типа конструкций одновременно

Опция drop представляет собой простой и эффективный механизм очистки производственных сборок от отладочного кода. Благодаря удалению операторов debugger и вызовов console итоговый бандл становится компактнее, чище и безопаснее для публикации в рабочей среде.