Export Icons
SXL Export Icons: installable CLI-утилита для экспорта COMPONENT/COMPONENT_SET из Figma с diff-sync, multi-config, шрифтами и SF Symbols.
Overview
SXL Export Icons (@sxl-studio/export-icons) — это CLI-утилита для массовой выгрузки иконок и ассетов из Figma в проект.
Утилита:
- экспортирует только
COMPONENTи варианты изCOMPONENT_SET; - поддерживает статические форматы
svg,png,jpg,webp,gif,pdf, а также Motionsvg,gif,mp4,webm; - делает инкрементальный sync (new/updated/deleted/renamed);
- умеет генерировать иконочный шрифт (
woff2/woff/ttf/svg/eot); - умеет собирать iOS
Assets.xcassets(.symbolset); - поддерживает сразу несколько конфигов и несколько source/target в одном конфиге;
- поддерживает 2 режима источника: Figma REST и Bridge/MCP.
Когда использовать
- Вы хотите автоматически синхронизировать иконки из Figma в
assets/. - Нужны стабильные имена файлов и управляемый нейминг (
kebab/snake/camel/pascal). - Нужен быстрый повторный запуск без полной перезагрузки.
- Нужны дополнительные артефакты: webfont, SF Symbols.
Установка
Требуется Node.js 20.9 или новее.
# локально в проект
npm install --save-dev @sxl-studio/export-icons
# запуск локально установленного бинарника
pnpm exec sxl-export-icons help
# one-shot без установки
pnpm dlx @sxl-studio/export-icons help
npx @sxl-studio/export-icons help
Не используйте
pnpx sxl-export-icons ...в корпоративных/proxy registry.
Такой вызов может пытаться резолвить unscoped пакетsxl-export-iconsи давать404.
Подготовка доступа к Figma
1) Получите Personal Access Token
- Откройте Figma account settings.
- Создайте Personal Access Token.
- В scopes включите минимум:
file_content:readfile_metadata:read
- Остальные scopes для
export-iconsне требуются (write-права не нужны). - Укажите срок действия токена согласно вашей политике (PAT в Figma ограничены по времени).
- Сохраните его в
.env:
FIGMA_TOKEN=figd_xxxxxxxxxxxxxxxxx
2) Получите fileKey
Из URL файла:
https://www.figma.com/design/<fileKey>/...
Сохраните ключ в .env:
SXL_ICONS_FILE_KEY=abc123xyz456
3) Зафиксируйте безопасность .env
Добавьте .env в .gitignore и не пушьте токены в git:
.env
.env.local
Минимальный набор переменных:
FIGMA_TOKEN=figd_xxxxxxxxxxxxxxxxx
SXL_ICONS_FILE_KEY=xxxxxxxxxxxx
# optional, only for mode: mcp
BRIDGE_AUTH_TOKEN=xxxxxxxxxxxx
CLI автоматически подхватывает .env.local и .env из текущей и родительских директорий.
Во время REST-загрузки теперь показывается живой прогресс по этапам/страницам (REST 1/3, REST 2/3, REST 3/3).
Быстрый старт
# 1. Создать шаблон конфига
pnpm exec sxl-export-icons init
# либо мастер настройки (интерактивный)
pnpm exec sxl-export-icons init --wizard
# 2. Проверить конфиг
pnpm exec sxl-export-icons validate-config --config sxl-export-icons.config.yaml
# 3. Dry-run
pnpm exec sxl-export-icons sync --config sxl-export-icons.config.yaml --dry-run
# 4. Первый реальный запуск (принять уже скачанные файлы как baseline)
pnpm exec sxl-export-icons sync --config sxl-export-icons.config.yaml --adopt-existing
# 5. Обычный sync
pnpm exec sxl-export-icons sync --config sxl-export-icons.config.yaml
# 6. Запуск только одного target из смешанного конфига
pnpm exec sxl-export-icons sync --config sxl-export-icons.config.yaml --target font-subset-local
Пример запуска из project terminal
cd ./design-system/tokens
pnpm exec sxl-export-icons sync --config ../sxl-export-icons.config.yaml --adopt-existing
pnpm exec sxl-export-icons sync --config ../sxl-export-icons.config.yaml
Рекомендуемые package scripts
Если вы запускаете утилиту из token package, добавьте похожие scripts в его package.json:
pnpm run icons:sync:init # первый запуск: baseline из существующих файлов
pnpm run icons:sync # повседневная синхронизация с Figma
pnpm run icons:sync:dry-run # предварительная проверка изменений
pnpm run icons:sync:full # аварийный полный пересбор
pnpm run icons:sync:font # локальная конвертация subset-иконок в шрифт
pnpm run icons:sync:font:dry-run
pnpm run icons:clean
Команды CLI
| Команда | Что делает |
|---|---|
sync (по умолчанию) | Экспорт/синхронизация иконок |
clean | Удаляет артефакты, зарегистрированные в state для выбранных scope |
init | Генерирует стартовый sxl-export-icons.config.yaml |
validate-config | Проверяет YAML и резолв переменных/путей |
report | Показывает покрытие state по source/target scope |
probe-motion | Диагностирует Figma Motion metadata и поддержку runtime export через Bridge/Remote Connect |
Алиасы команд:
create-config->initinit-wizard->init --wizard
Полезные флаги:
--config <path>(можно несколько раз)--config-glob "<glob>"--target <target-id>(можно несколько раз)--dry-run--full--adopt-existing--allow-fallback-rest--report <path-to-json>
Для clean есть дополнительные опции:
--skip-aux-> не удалять папки изfont.pathиsfSymbols.path, очищать только трекаемые ассет-файлы/state--dry-run-> показать, что будет удалено, без изменений на диске
Экспорт анимированных Motion-иконок
Figma Motion иконки можно экспортировать обычным sync, если в target явно задано motion.enabled: true.
Поддерживаемые Motion-форматы:
svg-> animated SVG, собранный из Motion keyframes;gif-> нативный Figma Motion GIF export через plugin runtime;mp4-> нативный Figma Motion MP4 export через plugin runtime;webm-> нативный Figma Motion WebM export через plugin runtime.
Для Motion export нужны SXL Studio Bridge, активная Remote Connect-сессия в том же Figma-файле и rest source с fileKey/fileKeyVar для обычной индексации файла или mcp source, когда файл уже открыт в Figma.
targets:
- id: motion-webm
sourceIds: [icons]
output:
path: assets/motion
format: webm
scale: 1
quality: 90
motion:
enabled: true
fps: 30
loop: true
ignoreOverlappingLayers: true
includeIdAttribute: true
Motion targets переэкспортируются при каждом sync, потому что Figma Motion keyframes читаются через plugin runtime и отсутствуют в REST file JSON fingerprints. Статические targets остаются на прежнем REST/SVG/raster export path.
Публичные Plugin typings/docs Figma не открывают MP4/WebM/GIF Motion formats, но текущий Figma Motion runtime принимает нативные exportAsync formats MP4, WEBM и GIF. Export Icons использует этот native path для media formats и plugin SVG_STRING + Motion keyframes для animated SVG. ignoreOverlappingLayers мапится в contentsOnly для SVG/SVG_STRING; текущий native Motion media runtime отвергает этот ключ, поэтому GIF/MP4/WebM используют runtime defaults Figma для overlap handling.
Motion runtime probe
probe-motion проверяет, что текущий Figma plugin runtime отдаёт для Motion nodes.
Нужны SXL Studio Bridge и активная Remote Connect-сессия в Figma. Без --node-id команда использует текущий selection в Figma.
pnpm exec sxl-export-icons probe-motion \
--format MP4,WEBM,GIF,SVG,SVG_STRING \
--ignore-overlapping-layers \
--include-id-attribute
Полезные опции:
--inspect-onlyчитает metadata без вызоваexportAsync.--format <list>принимает список через запятую или повторяющиеся флаги.--include-descendantsвключает descendant Motion keyframes, которые использует animated icon export.--ignore-overlapping-layersиспользуетcontentsOnly: trueдляSVG/SVG_STRING; native Motion media formats используют runtime defaults Figma.--include-id-attributeпередаётsvgIdAttribute: trueдляSVG/SVG_STRING.
Конфиг v3: структура
version: 3
env:
figmaTokenVar: FIGMA_TOKEN
bridge:
baseUrl: http://127.0.0.1:37830
authTokenVar: BRIDGE_AUTH_TOKEN
state:
file: .sxl/cache/icons-state.json
safety:
allowOutsideWorkspace: false
sources: []
targets: []
Важно про state.file:
- дефолт схемы —
assets/.cache/sxl-export-icons-state.json; - шаблон
init/wizard предлагает.sxl/cache/icons-state.json.
Оба варианта валидны. Выберите один стабильный путь для репозитория.
Зачем нужен bridge в конфиге
bridge относится к Utils/bridge и нужен для sources[].mode: mcp, а также для targets с motion.enabled.
Если вы работаете только со статическими targets через mode: rest, блок bridge игнорируется.
Мастер настройки (init --wizard)
Интерактивный мастер задаёт базовые вопросы и собирает валидный конфиг:
- язык (
en/ru, по умолчаниюen); - создавать ли
.env; - путь к
state.file; safety.allowOutsideWorkspace;- базовые параметры
sourceиtarget; - включать ли font pipeline / SF Symbols;
- включать ли фильтр по marker в
description.
Команда:
pnpm exec sxl-export-icons init --wizard
Поддерживается и legacy-конфиг version: 2 (icons.config.yaml) через авто-миграцию.
Для version: 2 относительные пути резолвятся от корня workspace (поведение старого скрипта сохраняется).
sources[] — откуда читать
sources:
- id: mono
mode: rest # rest | mcp
fileKeyVar: SXL_ICONS_FILE_KEY
pageName: Monochrome
sectionName: Actions
downloadSpeed: 20
selectors:
includeNodeIds: []
excludeNodeIds: []
includeComponentSetNames: []
excludeComponentSetNames: []
includeComponentNames: []
excludeComponentNames: []
variantMatch:
styleMode: outlined
Ключевые поля:
mode: rest— прямой Figma REST API.mode: mcp— чтение через SXL Bridge Remote Connect.fileKey/fileKeyVar— обязателен для REST.pageName|pageId,sectionName|sectionId— фильтр области.selectors.variantMatch— матч по любым variant props (не толькоstyle).- При
sectionName|sectionIdв экспорт попадают только узлы внутри выбранной секции.
Справочник selectors:
includeNodeIds/excludeNodeIds-> allow/deny по конкретным node IDincludeComponentNames/excludeComponentNames-> allow/deny по именам COMPONENTincludeComponentSetNames/excludeComponentSetNames-> allow/deny по именам COMPONENT_SETvariantMatch-> точный матч по variant props; значение может быть строкой или массивом строк
Пример variantMatch с несколькими значениями:
selectors:
variantMatch:
state: [default, hover]
styleMode: outlined
Фильтр по marker в description
Можно включить фильтр экспорта по описанию COMPONENT / COMPONENT_SET:
sources:
- id: mono
mode: rest
fileKeyVar: SXL_ICONS_FILE_KEY
descriptionExportMarker:
key: sxl-studio-export-icon
includeWhenMissing: true
Правила:
- если в
descriptionестьsxl-studio-export-icon: true-> экспортируется; - если есть
sxl-studio-export-icon: false-> пропускается; - если marker отсутствует -> поведение берётся из
includeWhenMissing.
targets[] — куда и как писать
targets:
- id: web-icons
mode: sync # sync | convert-local
sourceIds: [mono]
selectors:
excludeComponentNames: [deprecated-icon]
download:
enabled: true
pruneUntracked: false
output:
path: assets/icons
format: svg
scale: 1
quality: 100
qualitySize: null
width: null
height: null
modeWH: null
naming:
case: kebab
separator: "-"
pattern: "{prefix}{variant.styleMode}{sectionName}{baseName}{suffix}"
prefix: null
suffix: null
includeSectionName: false
collisionStrategy: add-nodeid
Фильтрация в target:
targets[].selectorsфильтрует элементы source перед расчетом output/naming.targets[].overrides[].patch.selectorsдобавляет фильтр только для конкретного override-правила.
Один или несколько форматов
target.output.format задаёт один формат на один target.
Если нужно выгружать одни и те же иконки сразу в несколько форматов, создайте несколько target с одним и тем же sourceIds:
targets:
- id: mono-svg
sourceIds: [mono]
output:
path: assets/icons/svg
format: svg
scale: 1
quality: 100
qualitySize: null
width: null
height: null
modeWH: null
- id: mono-webp
sourceIds: [mono]
output:
path: assets/icons/webp
format: webp
scale: 1
quality: 90
qualitySize: null
width: null
height: null
modeWH: null
Нейминг и шаблоны
naming.pattern использует токены:
{prefix},{suffix}{sectionName},{pageName}{baseName},{componentName},{componentSetName},{nodeName},{nodeId}{variant}(все variant props){variant.<propName>}(любой конкретный variant prop)
Правила для variant:
variant— зарезервированное имя пространства токенов (менять его нельзя).<propName>— произвольное имя variant-свойства из Figma (напримерstyle,type,size,state).- Для нескольких props просто добавляйте несколько токенов в нужном порядке:
"{prefix}{pageName}{variant.style}{variant.type}{baseName}"
- Если у конкретного узла такого props нет, этот сегмент пропускается.
Это может создать коллизию имён, поэтому для mixed-наборов используйте
collisionStrategyи/или добавляйте другие токены ({sectionName},{nodeId}).
Пример:
pattern: "{prefix}{variant.styleMode}{baseName}{suffix}"
Политика коллизий:
error— завершить с ошибкой;add-nodeid— добавить suffix изnodeId;add-index— добавить порядковый индекс.
Интерактивный preflight дублей
Перед скачиванием, если обнаружены дубли имён, CLI выводит список дублей и предлагает действие:
refresh— перечитать source и перепроверить дублиskip— оставить один узел, дубли пропуститьall— загрузить все дубли с автопереименованием поcollisionStrategyabort— остановить sync
В интерактивном терминале выбор делается клавиатурой (↑/↓ + Enter), без ручного ввода команды.
В неинтерактивном режиме (CI) prompt пропускается, применяется collisionStrategy из конфига.
Переопределения (overrides)
Переопределения позволяют менять output / naming / font / sfSymbols по match-правилам:
overrides:
- name: flags-webp
match:
sectionName: Flags
patch:
output:
format: webp
quality: 85
- name: outlined-only
match:
variantMatch:
styleMode: outlined
patch:
naming:
suffix: outlined
selectors:
excludeComponentNames: [legacy-icon]
- name: ios-symbolset-only
match:
pageName: iOS
patch:
sfSymbols:
enabled: true
font:
enabled: false
Поддерживаемые поля match:
pageName,pageIdsectionName,sectionIdcomponentName,componentSetNamenodeIdvariantMatch
Multi-config и multi-source
Несколько конфигов за один запуск
npx sxl-export-icons sync \
--config packages/ds-a/sxl-export-icons.config.yaml \
--config packages/ds-b/sxl-export-icons.config.yaml
Несколько источников в одном конфиге
sources:
- id: mono
mode: rest
fileKeyVar: FILE_A
- id: flags
mode: rest
fileKeyVar: FILE_B
targets:
- id: web
sourceIds: [mono, flags]
output:
path: assets/icons
format: svg
scale: 1
quality: 100
qualitySize: null
width: null
height: null
modeWH: null
naming:
case: kebab
collisionStrategy: add-nodeid
Для нескольких страниц/секций одного и того же Figma-файла создайте несколько source с одним fileKeyVar, но разными pageName/sectionName.
Выгрузка за пределы текущей папки проекта
По умолчанию заблокирована (безопасность).
Чтобы разрешить:
safety:
allowOutsideWorkspace: true
allowOutsideWorkspace: false (по умолчанию) защищает от случайной записи файлов за пределы репозитория.
Включайте true, только если осознанно пишете в абсолютные/внешние пути.
После этого можно писать, например, из packages/ds/tokens в packages/ds/assets.
Font и SF Symbols
Генерация шрифта
font:
enabled: true
path: assets/icons/font
formats: [woff2, woff, ttf, eot, svg]
fontName: icons
engine: webfonts-generator # webfonts-generator | advanced
includeNames: [] # опционально: выборка по canonical именам
excludeNames: [] # опционально: исключения
Генерация SF Symbols
sfSymbols:
enabled: true
path: assets/icons/ios
Ограничение: сложные SVG (градиенты, фильтры, маски, embedded image) пропускаются как несовместимые.
Режим только конвертации (локальные SVG -> font/SF)
Если SVG уже лежат в output-папке и нужна только конвертация:
targets:
- id: icons-font-only
mode: convert-local
sourceIds: []
download:
enabled: false
output:
path: assets/icons
format: svg
scale: 1
quality: 100
qualitySize: null
width: null
height: null
modeWH: null
naming:
case: kebab
separator: "-"
pattern: "{variant.style}{baseName}"
includeSectionName: false
collisionStrategy: add-nodeid
font:
enabled: true
path: assets/icons/font
formats: [woff2, woff, ttf, eot]
fontName: icons
includeNames: [filled-casino-menu, outline-casino-menu]
download.enabled: false отключает download/rename/delete и использует уже существующие локальные SVG.
font.includeNames / font.excludeNames можно указывать как с .svg, так и без расширения.
Clean и отчеты
Очистка трекаемых файлов по выбранным scope:
pnpm exec sxl-export-icons clean --config ./sxl-export-icons.config.yaml
Очистка без удаления папок шрифта/SF:
pnpm exec sxl-export-icons clean --config ./sxl-export-icons.config.yaml --skip-aux
Предпросмотр очистки:
pnpm exec sxl-export-icons clean --config ./sxl-export-icons.config.yaml --dry-run
Отчет по покрытию state:
pnpm exec sxl-export-icons report --config ./sxl-export-icons.config.yaml
pnpm exec sxl-export-icons sync --config ./sxl-export-icons.config.yaml --report ./.reports/icons-report.json
Diff и state
State хранится в state.file и ведется по scope sourceId::targetId.
Типы изменений:
NEWUPDATEDDELETEDRENAMEDUNCHANGED
UPDATED также срабатывает при смене формата (например svg -> webp) или при изменении hash конфигурации scope (source + target), чтобы сделать корректный re-export.
Для REST-режима используется fingerprint по render-данным узла (geometry=paths), поэтому изменения вектора (шейпа) детектируются и переэкспортируются даже при неизменном timestamp в metadata.
Первый запуск без state считает все найденные иконки NEW (полный re-export), даже если файлы уже есть на диске.
Если нужно принять уже существующие файлы как baseline UNCHANGED, используйте --adopt-existing на первом реальном запуске.
Опциональная строгая очистка:
- включите
download.pruneUntracked: true, чтобы удалять неотслеживаемые файлы того же формата вoutput.path; - используйте только для выделенных директорий (очистка работает по path+format).
Режимы источника: REST vs MCP
REST
Плюсы:
- стабильный и предсказуемый;
- не требует активной Remote Connect сессии для статических targets;
- Motion targets всё равно требуют Bridge/Remote Connect, потому что keyframes читаются через plugin runtime.
MCP (Bridge)
Плюсы:
- чтение через активный контекст плагина;
- удобно для оркестрации сценариев с агентами.
Требования:
- запущен
Utils/bridge; - включен Remote Connect в SXL Studio plugin;
- в конфиге задан
bridge.baseUrl.
Если указать --allow-fallback-rest, при проблеме MCP источник автоматически переключится на REST (если есть fileKey).
Режимы target: sync vs convert-local
mode: sync(по умолчанию): читает Figma source, синхронизирует файлы, затем запускает пост-обработку.mode: convert-local: не читает Figma source, использует уже существующие локальные SVG изoutput.pathдля генерации font/SF.
Требования для convert-local:
sourceIds: []download.enabled: falseoutput.format: svg- включен
fontилиsfSymbols
Типовые ошибки
-
Missing Figma token
Проверьте переменную изenv.figmaTokenVar. -
outside workspace
Установитеsafety.allowOutsideWorkspace: true, если это ожидаемо. -
Bridge session is not connected
Запустите Bridge и включите Remote Connect в плагине. -
Naming collision detected
Обновитеnaming.patternили включитеcollisionStrategy: add-nodeid/add-index.
Лучшие практики
- Храните секреты (
FIGMA_TOKEN, file keys) только в.env. - Используйте
--dry-runперед большим sync. - Для больших библиотек разделяйте источники (
sources) по страницам. - Для публичных пакетов ассетов фиксируйте
naming.patternи не меняйте без migration-ноты. - В CI сохраняйте
--reportJSON как артефакт сборки.