/** * Браузеры, поддерживаемые esbuild/Oxc. * * Зачем: browserslist как входной конфиг должен быть приведён к целевому * формату, который понимает минификатор/трансформер. У esbuild и Oxc * список целевых браузеров жёстко ограничен и отличается от browserslist. * Поэтому мы транслируем браузеры из browserslist в короткий esbuild-формат, * а всё, чего в нём нет — отбрасываем. */ const SUPPORTED_TARGETS = new Set(['es', 'chrome', 'edge', 'firefox', 'ios', 'node', 'safari', 'opera', 'ie']); /** * Соответствие идентификаторов браузеров browserslist тем, что знает esbuild. * * Browserslist различает `ios_saf` (Safari на iOS) и `android` (Chrome на * Android), тогда как esbuild знает общее `ios` и `chrome`. */ const REPLACES = { ios_saf: 'ios', android: 'chrome', }; /** * Преобразует результат `browserslist()` в массив target-строк для esbuild/Oxc. * * Вход — массив записей вида `"chrome 109"`, `"ios_saf 18.5-18.7"`. * Выход — массив вида `["chrome109", "ios18.5"]`. * * Шаги: * 1. Режем запись на имя и версию. * 2. Подменяем идентификаторы (`ios_saf → ios`, `android → chrome`). * 3. Диапазоны версий сводим к нижней границе (`18.5-18.7 → 18.5`). * 4. Приводим `18.0` к `18` (убираем хвостовой `.0`). * 5. Отбрасываем версии, не проходящие regex (ломают esbuild), и браузеры, * которых нет в `SUPPORTED_TARGETS` (`and_chr`, `kaios`, `samsung` и т.д.). * 6. Для каждого браузера оставляем самую старую версию — esbuild применяет * трансформации, покрывающие самую консервативную цель. * 7. Собираем целевые строки без пробела: `chrome109`. * * @param {string[]} browsers - массив строк браузеров от `browserslist()`. * @returns {string[]} массив target-строк совместимый с esbuild/Oxc. */ export const browserslistToTargets = (browsers) => { const result = new Map(); for (const browser of browsers) { // Шаг 1: ['chrome', '109']. let [name, version] = browser.split(' '); // Шаг 2: подмена идентификаторов. name = REPLACES[name] ?? name; // Шаг 3: диапазон → нижняя граница. version = version.includes('-') ? version.slice(0, version.indexOf('-')) : version; // Шаг 4: `18.0` → `18`, но `18.5` не должно трогаться. version = version.endsWith('.0') ? version.slice(0, -2) : version; // Шаг 5: валидная версия + поддерживаемый браузер. if (/^\d+(\.\d+)*$/.test(version) && SUPPORTED_TARGETS.has(name)) { // Шаг 6: самая старая версия (последняя в списке) перезатирает предыдущую. result.set(name, version); } } // Шаг 7: собираем без пробела. return [...result].map(([name, version]) => `${name}${version}`); };