При сборке приложений нередко возникает необходимость автоматически
удалять отладочный код из финального бандла. В процессе разработки
активно используются инструкции debugger, а также различные
вызовы console.log(), console.warn(),
console.error() и других методов объекта
console. Эти конструкции помогают анализировать работу
приложения, однако в производственной сборке зачастую оказываются
ненужными.
Для решения этой задачи в Esbuild предусмотрена опция
drop, позволяющая удалять определённые виды инструкций ещё
на этапе трансформации исходного кода.
Поддерживаются два значения:
debuggerconsoledebuggerОператор 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 полностью удаляются из итогового
кода.
Удаление операторов 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 отвечает за устранение конкретных конструкций.
Иногда возникает путаница между опцией 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
сохраняются без изменений.
В производственной сборке такие конструкции автоматически исчезают.
Нередко опция drop комбинируется с заменой констант
через define.
Пример:
await esbuild.build({
bundle: true,
define: {
__DEV__: 'false'
},
drop: ['debugger', 'console']
})
Исходный код:
if (__DEV__) {
console.log('Development mode')
}
После обработки:
if (false) {
}
Далее минификатор и механизмы оптимизации могут полностью удалить недостижимый участок кода.
Для большинства веб-приложений оправдано использование:
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
итоговый бандл становится компактнее, чище и безопаснее для публикации в
рабочей среде.