66 lines
3.6 KiB
JavaScript
66 lines
3.6 KiB
JavaScript
/**
|
||
* Браузеры, поддерживаемые 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}`);
|
||
};
|