diff --git a/packages/browserslist-to-targets/README.md b/packages/browserslist-to-targets/README.md new file mode 100644 index 0000000..525fdd8 --- /dev/null +++ b/packages/browserslist-to-targets/README.md @@ -0,0 +1,47 @@ +# @advdominion/browserslist-to-targets + +Преобразует конфиг [browserslist](https://github.com/browserslist/browserslist) в массив target-строк, совместимый с [esbuild](https://esbuild.github.io) и [Oxc](https://oxc-project.github.io). + +## Зачем + +Оба инструмента (esbuild и Oxc) понимают ограниченный набор целевых браузеров, который отличается от browserslist. Пакет приводит результат `browserslist()` к короткому esbuild-формату: `["chrome109", "ios18.5"]`, а всё, что esbuild/Oxc не поддерживает (`and_chr`, `kaios`, `samsung` и т.д.) — отбрасывает. + +## Установка + +```bash +yarn add -D @advdominion/browserslist-to-targets +``` + +## Использование + +```js +import browserslist from 'browserslist'; +import { browserslistToTargets } from '@advdominion/browserslist-to-targets'; + +const targets = browserslistToTargets(browserslist()); +// ["chrome109", "edge149", "firefox140", "ios18.5", "opera127", "safari26.5"] +``` + +## API + +### `browserslistToTargets(browsers): string[]` + +- `browsers` — массив строк от `browserslist()`, например `["chrome 151", "ios_saf 26.6"]`. +- Возвращает массив target-строк без пробела: `["chrome151", "ios26.5"]`. + +## Правила преобразования + +| browserslist | esbuild/Oxc | Примечание | +| ------------------------------------------------- | --------------- | ------------------------------------------ | +| `chrome 151` | `chrome151` | версия становится «приклеенной» к имени | +| `ios_saf 26.6` | `ios26.5` | `ios_saf → ios`, остаётся самая старая | +| `ios_saf 25-26.5` | `ios25` | `ios_saf → ios`, диапазон → нижняя граница | +| `android 151` | `chrome151` | `android → chrome` | +| `ios 26.0` | `ios26` | убирает завершающий `.0` | +| `and_chr`, `kaios`, `samsung`, `op_mob`, `and_ff` | — отбрасывается | нет в esbuild/Oxc | + +Для каждого браузера остаётся самая старая версия — трансформации покрывают самую консервативную цель. + +## Лицензия + +MIT diff --git a/packages/browserslist-to-targets/index.js b/packages/browserslist-to-targets/index.js new file mode 100644 index 0000000..2599e84 --- /dev/null +++ b/packages/browserslist-to-targets/index.js @@ -0,0 +1,65 @@ +/** + * Браузеры, поддерживаемые 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}`); +}; diff --git a/packages/browserslist-to-targets/package.json b/packages/browserslist-to-targets/package.json new file mode 100644 index 0000000..d75b689 --- /dev/null +++ b/packages/browserslist-to-targets/package.json @@ -0,0 +1,14 @@ +{ + "name": "@advdominion/browserslist-to-targets", + "version": "1.0.0", + "type": "module", + "main": "index.js", + "repository": { + "type": "git", + "url": "https://gitea.optiweb.ru/public/frontend.git" + }, + "license": "MIT", + "publishConfig": { + "access": "public" + } +} diff --git a/yarn.lock b/yarn.lock index 7c9dfd5..f8247fa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14,6 +14,12 @@ __metadata: languageName: unknown linkType: soft +"@advdominion/browserslist-to-targets@workspace:packages/browserslist-to-targets": + version: 0.0.0-use.local + resolution: "@advdominion/browserslist-to-targets@workspace:packages/browserslist-to-targets" + languageName: unknown + linkType: soft + "@advdominion/css-var@workspace:packages/css-var": version: 0.0.0-use.local resolution: "@advdominion/css-var@workspace:packages/css-var"