Минификация в контексте библиотек отличается от аналогичного процесса в приложениях. Если в приложениях основная цель — уменьшение размера бандла для ускорения загрузки конечного продукта, то в библиотеках добавляется дополнительный слой требований: предсказуемость поведения кода у потребителя, совместимость с tree-shaking и отсутствие разрушения метаданных, влияющих на работу сборщиков downstream-проектов.
Минифицированный код в библиотеке часто поставляется как отдельный артефакт, параллельно с неминифицированной версией. Это связано с тем, что потребитель библиотеки может сам применять минификацию в своем pipeline, и двойная минификация приводит к ухудшению читаемости и потенциальной потере оптимизаций.
Ключевая особенность библиотечной минификации заключается в необходимости сохранять семантическую эквивалентность модулей без изменения структуры экспорта.
Разные форматы требуют различного подхода к минификации.
ESM (ECMAScript Modules) Минификация ESM-сборки должна учитывать tree-shaking. Любое агрессивное преобразование может нарушить статический анализ импортов. Например, объединение модулей или переименование экспортов на уровне бандла может ухудшить удаление мертвого кода.
CommonJS (CJS) CJS менее чувствителен к
tree-shaking, но минификация должна сохранять корректную структуру
require-вызовов. Здесь допускается более агрессивная
трансформация кода, включая инлайнинг и переупорядочивание
выражений.
UMD UMD-формат чаще всего используется для браузерного подключения без сборщика. Минификация здесь наиболее агрессивна, так как предполагается прямое использование. Однако важно сохранять глобальное имя библиотеки.
Минификация в Rollup реализуется через плагины, поскольку сам Rollup не выполняет сжатие кода.
Типичный pipeline включает:
Минификация обычно выполняется на последнем этапе.
Пример конфигурации:
import { defineConfig } from 'rollup';
import terser from '@rollup/plugin-terser';
export default defineConfig({
input: 'src/index.js',
output: [
{
file: 'dist/library.esm.js',
format: 'esm',
sourcemap: true
},
{
file: 'dist/library.esm.min.js',
format: 'esm',
sourcemap: true,
plugins: [terser()]
}
]
});
В данном случае создаются две версии: обычная и минифицированная, что является распространенной практикой для библиотек.
Terser остается стандартом де-факто для JavaScript
минификации в Rollup-пайплайнах.
Основные возможности:
Конфигурация:
terser({
format: {
comments: false
},
compress: {
drop_console: true,
passes: 2
},
mangle: {
keep_fnames: false
}
});
В библиотечном контексте параметры требуют осторожности. Например,
mangle может нарушить работу reflection-based API или
сериализацию ошибок.
esbuild часто используется как более быстрый
минификатор. Его основное преимущество — скорость, особенно на больших
кодовых базах.
Использование в Rollup:
import esbuild from 'rollup-plugin-esbuild';
export default {
input: 'src/index.ts',
output: {
file: 'dist/library.min.js',
format: 'esm'
},
plugins: [
esbuild({
minify: true,
target: 'es2018'
})
]
};
Особенности esbuild:
SWC занимает промежуточную позицию между Terser и
esbuild. Он быстрее Terser и более гибкий, чем esbuild в некоторых
сценариях.
Пример:
import swc from 'rollup-plugin-swc';
export default {
plugins: [
swc({
minify: true,
jsc: {
minify: {
compress: true,
mangle: true
}
}
})
]
};
SWC часто используется в монорепозиториях, где важна унификация инструментов трансформации и минификации.
Минификация может как улучшить, так и ухудшить результаты tree-shaking.
Положительное влияние:
Отрицательное влияние:
В библиотечном контексте важно не полагаться на минификацию как на основной механизм tree-shaking. Основная оптимизация должна происходить до нее.
Поле sideEffects в package.json критично
для корректной минификации и tree-shaking.
{
"sideEffects": false
}
При таком значении сборщик может безопасно удалять неиспользуемые модули. Однако минификация не должна изменять поведение side effects.
Ошибки возникают, когда:
Для библиотек критично сохранение публичного API.
Проблемные случаи:
mangle переименовывает экспортируемые функцииinstanceof)Решение:
terser({
mangle: {
keep_classnames: true,
keep_fnames: true
}
});
В некоторых библиотеках полностью отключается mangling для сохранения стабильности API.
Source maps становятся обязательным элементом при минификации библиотек.
Особенности:
Конфигурация:
output: {
sourcemap: true
}
При использовании Terser:
terser({
sourceMap: true
})
Важно учитывать, что sourcemap увеличивает размер артефактов, но в библиотечном контексте это часто приемлемо.
Стандартная практика библиотек — поставка двух версий:
Причины:
Пример структуры:
dist/
index.esm.js
index.esm.min.js
index.cjs.js
index.cjs.min.js
Некоторые трансформации могут ломать совместимость:
Библиотечная минификация требует консервативного подхода. Часто используется режим:
Порядок выполнения плагинов критичен:
Нарушение порядка приводит к:
В монорепозиториях часто используется единый слой минификации для всех пакетов.
Особенности:
Минификация может быть вынесена в отдельный шаг CI, а не в Rollup pipeline, чтобы ускорить локальную разработку.
Распространенные стратегии:
Пример разделения:
const isProd = process.env.NODE_ENV === 'production';
export default {
plugins: [
isProd && terser({
compress: true,
mangle: true
})
]
};
Типичные ошибки:
Каждая из этих ошибок приводит либо к увеличению размера бандла, либо к нарушению корректности работы библиотеки в downstream проектах.