Параметр drop в Esbuild предназначен для удаления
определённых конструкций из исходного кода ещё на этапе сборки. Это
позволяет уменьшить размер итогового бандла, устранить лишние побочные
действия и подготовить приложение к производственной эксплуатации.
Опция работает на уровне синтаксического анализа и удаляет заранее
определённые конструкции независимо от того, используются ли они в
дальнейшем коде. В отличие от механизмов удаления неиспользуемого кода
(Tree Shaking), drop выполняет принудительное исключение
указанных элементов.
Общий вид настройки:
await esbuild.build({
entryPoints: ['src/index.js'],
bundle: true,
drop: ['console']
})
Параметр принимает массив строк, каждая из которых соответствует определённому логическому оператору удаления.
На текущий момент Esbuild поддерживает два значения для параметра
drop:
drop: ['console']
и
drop: ['debugger']
Также допускается одновременное использование нескольких значений:
drop: ['console', 'debugger']
consoleЗначение console удаляет вызовы методов объекта
console.
Пример исходного кода:
function calculate(a, b) {
console.log('Начало вычислений')
const result = a + b
console.info('Результат:', result)
return result
}
После сборки:
function calculate(a, b) {
const result = a + b
return result
}
Все вызовы логирования будут исключены.
Удаляются все обращения к стандартному объекту консоли:
console.log()
console.info()
console.warn()
console.error()
console.debug()
console.trace()
console.dir()
console.table()
Пример:
console.log('message')
console.error('error')
console.warn('warning')
После обработки:
Если в файле присутствовали только вызовы консоли, итоговый код может оказаться пустым.
Во многих проектах количество диагностических сообщений может исчисляться сотнями или тысячами строк.
Например:
console.log(user)
console.log(settings)
console.log(response)
console.log(cache)
console.log(state)
После удаления:
Чем больше логирования используется во время разработки, тем заметнее становится уменьшение размера итогового файла.
Исходный код:
function processData(data) {
console.log('processing')
return data.map(item => {
console.log(item)
return item * 2
})
}
Результат:
function processData(data) {
return data.map(item => {
return item * 2
})
}
Удаление выполняется независимо от уровня вложенности.
Необходимо учитывать важную особенность. При использовании
drop: ['console'] удаляется весь вызов консоли вместе с
вычислением его аргументов.
Пример:
console.log(expensiveOperation())
После сборки:
Функция expensiveOperation() также не будет вызвана.
Это отличается от ситуаций, когда логирование отключается вручную:
if (DEBUG) {
console.log(expensiveOperation())
}
В таком случае результат зависит от последующей оптимизации и удаления мёртвого кода.
Следует соблюдать осторожность при передаче в консоль выражений с побочными эффектами.
Исходный код:
console.log(updateDatabase())
После применения:
Функция updateDatabase() больше не будет
выполняться.
Другой пример:
console.log(counter++)
После удаления:
Инкремент также исчезнет.
По этой причине вызовы с важной бизнес-логикой не рекомендуется помещать внутрь аргументов методов консоли.
Конфигурация для production-сборки:
await esbuild.build({
entryPoints: ['src/index.js'],
bundle: true,
minify: true,
drop: ['console']
})
Для режима разработки параметр обычно отключается:
await esbuild.build({
entryPoints: ['src/index.js'],
bundle: true
})
debuggerЗначение debugger удаляет все инструкции
debugger.
Исходный код:
function login(user) {
debugger
validate(user)
}
После сборки:
function login(user) {
validate(user)
}
debuggerИнструкция предназначена для временной остановки выполнения программы в инструментах разработчика браузера или среды исполнения.
Пример:
function calculatePrice(product) {
debugger
return product.price * product.quantity
}
Во время отладки выполнение будет остановлено на этой строке.
После завершения разработки подобные инструкции обычно удаляются.
Вместо ручного поиска можно использовать:
await esbuild.build({
entryPoints: ['src/index.js'],
bundle: true,
drop: ['debugger']
})
Любые инструкции:
debugger
будут исключены из выходного файла.
Исходный код:
if (user.isAdmin) {
debugger
openAdminPanel()
}
После сборки:
if (user.isAdmin) {
openAdminPanel()
}
Исходный код:
for (const item of items) {
debugger
process(item)
}
После обработки:
for (const item of items) {
process(item)
}
Пример:
function outer() {
function inner() {
debugger
return true
}
return inner()
}
После сборки:
function outer() {
function inner() {
return true
}
return inner()
}
Наиболее распространённый вариант production-конфигурации:
await esbuild.build({
entryPoints: ['src/index.js'],
bundle: true,
minify: true,
drop: ['console', 'debugger']
})
Исходный код:
console.log('application start')
debugger
startApplication()
Результат:
startApplication()
Удаление консоли:
esbuild app.js --bundle --drop:console
Удаление отладчиков:
esbuild app.js --bundle --drop:debugger
Одновременное удаление:
esbuild app.js --bundle --drop:console --drop:debugger
Удаление только консоли:
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['src/index.js'],
outfile: 'dist/app.js',
drop: ['console']
})
Удаление только отладчиков:
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['src/index.js'],
outfile: 'dist/app.js',
drop: ['debugger']
})
Удаление обеих конструкций:
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['src/index.js'],
outfile: 'dist/app.js',
drop: ['console', 'debugger']
})
Наиболее эффективный результат достигается при совместном использовании с минификацией.
Пример:
await esbuild.build({
entryPoints: ['src/index.js'],
bundle: true,
minify: true,
drop: ['console', 'debugger']
})
Последовательность обработки обычно выглядит следующим образом:
drop.Такой подход позволяет минификатору работать уже с очищенным кодом и получать более компактный результат.
drop от
defineИногда параметры drop и define решают
схожие задачи, но работают по-разному.
Пример с define:
if (DEBUG) {
console.log('message')
}
Конфигурация:
define: {
DEBUG: 'false'
}
После оптимизации код может быть удалён как недостижимый.
Параметр drop действует иначе:
console.log('message')
Конфигурация:
drop: ['console']
Вызов удаляется без каких-либо дополнительных условий.
drop от Tree
ShakingTree Shaking анализирует импортируемые сущности и устраняет неиспользуемый код.
Пример:
import { used, unused } from './utils.js'
used()
Функция unused() может быть исключена из бандла.
Параметр drop не занимается анализом зависимостей и
предназначен исключительно для удаления заранее определённых
конструкций:
console.log('debug')
debugger
Они будут удалены независимо от того, используются ли остальные части программы.
Для production-сборок
drop: ['console', 'debugger']
Позволяет убрать отладочный код и уменьшить размер выходных файлов.
Для development-сборок
drop: []
Сохраняет сообщения логирования и точки останова для диагностики.
Избегание побочных эффектов
Нежелательно писать:
console.log(saveUser())
Безопаснее разделять действия:
saveUser()
console.log('User saved')
Тогда удаление консоли не изменит поведение программы.
Использование в CI/CD
Часто параметр включается только для производственных сборок:
const production = process.env.NODE_ENV === 'production'
await esbuild.build({
entryPoints: ['src/index.js'],
bundle: true,
drop: production
? ['console', 'debugger']
: []
})
Такой подход позволяет сохранять максимальное количество диагностической информации во время разработки и автоматически очищать код при публикации приложения.