mirror of
https://github.com/gromlab-ru/svg-sprites.git
synced 2026-07-22 04:40:17 +03:00
sync
This commit is contained in:
79
README_RU.md
79
README_RU.md
@@ -1,6 +1,6 @@
|
|||||||
# @gromlab/svg-sprites
|
# @gromlab/svg-sprites
|
||||||
|
|
||||||
[🇬🇧 English](README.md) | 🇷🇺 Русский
|
[🇬🇧 English](https://github.com/gromlab-ru/svg-sprites/blob/master/README.md) | 🇷🇺 Русский
|
||||||
|
|
||||||
 
|
 
|
||||||
|
|
||||||
@@ -24,20 +24,6 @@
|
|||||||
|
|
||||||
В приложении не приходится работать со спрайтом напрямую. Вы используете его так же, как обычную SVG-иконку, но получаете один компонент, автокомплит и TypeScript-проверку всех имён.
|
В приложении не приходится работать со спрайтом напрямую. Вы используете его так же, как обычную SVG-иконку, но получаете один компонент, автокомплит и TypeScript-проверку всех имён.
|
||||||
|
|
||||||
В `standalone@vite` и `standalone@webpack` тот же подход доступен без React:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineAppIconElement } from './app-icons'
|
|
||||||
|
|
||||||
defineAppIconElement()
|
|
||||||
```
|
|
||||||
|
|
||||||
```html
|
|
||||||
<app-icon icon="search" style="font-size: 24px"></app-icon>
|
|
||||||
```
|
|
||||||
|
|
||||||
Bare `standalone` остаётся минимальным и генерирует только SVG asset и JSON manifest без JavaScript runtime.
|
|
||||||
|
|
||||||
## AI-friendly из коробки
|
## AI-friendly из коробки
|
||||||
|
|
||||||
`@gromlab/svg-sprites` сразу рассчитан на работу с AI-агентами. Подключите готовый skill и поручите агенту настройку, миграцию или диагностику без длинных инструкций и ручного изучения документации.
|
`@gromlab/svg-sprites` сразу рассчитан на работу с AI-агентами. Подключите готовый skill и поручите агенту настройку, миграцию или диагностику без длинных инструкций и ручного изучения документации.
|
||||||
@@ -46,61 +32,54 @@ Bare `standalone` остаётся минимальным и генерируе
|
|||||||
|
|
||||||
[🇬🇧 Скачать AI skill (на английском)](https://github.com/gromlab-ru/svg-sprites/releases/latest/download/svg-sprites.zip)
|
[🇬🇧 Скачать AI skill (на английском)](https://github.com/gromlab-ru/svg-sprites/releases/latest/download/svg-sprites.zip)
|
||||||
|
|
||||||
## От SVG до компонента за четыре шага
|
## От SVG до компонента за три шага
|
||||||
|
|
||||||
Основной пример использует Next.js App Router и Turbopack.
|
Основной пример использует Next.js App Router и Turbopack.
|
||||||
|
|
||||||
### 1. Генерируйте без установки пакета
|
### 1. Укажите нужные иконки
|
||||||
|
|
||||||
```bash
|
Создайте папки для исходных иконок и спрайта:
|
||||||
npx --yes --package=@gromlab/svg-sprites@latest svg-sprites --help
|
|
||||||
```
|
|
||||||
|
|
||||||
`npx` временно скачивает CLI, не добавляет `@gromlab/svg-sprites` в
|
|
||||||
`package.json`, а generated production runtime не импортирует package.
|
|
||||||
|
|
||||||
### 2. Укажите нужные иконки
|
|
||||||
|
|
||||||
SVG могут оставаться в существующей структуре проекта:
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
src/
|
assets/
|
||||||
├── assets/icons/
|
├── app-icons/
|
||||||
│ ├── search.svg
|
│ └── svg-sprite.config.json
|
||||||
│ └── settings.svg
|
└── svg-icons/
|
||||||
├── features/profile/
|
├── search.svg
|
||||||
│ └── user.svg
|
└── settings.svg
|
||||||
└── ui/app-icons/
|
|
||||||
└── svg-sprite.config.ts
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Создайте конфигурацию спрайта:
|
Создайте конфигурацию спрайта:
|
||||||
|
|
||||||
```ts
|
```json
|
||||||
// src/ui/app-icons/svg-sprite.config.ts
|
{
|
||||||
export default {
|
"mode": "next@app/turbopack",
|
||||||
mode: 'next@app/turbopack',
|
"name": "app",
|
||||||
name: 'app',
|
"input": "../svg-icons/**/*.svg"
|
||||||
input: [
|
|
||||||
'../../assets/icons/search.svg',
|
|
||||||
'../../assets/icons/settings.svg',
|
|
||||||
'../../features/profile/user.svg',
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. Добавьте генерацию
|
`input` поддерживает пути к папкам, отдельным SVG и glob-шаблоны.
|
||||||
|
|
||||||
|
### 2. Добавьте генерацию
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/app-icons/svg-sprite.config.ts",
|
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
|
||||||
"predev": "npm run sprites",
|
"predev": "npm run sprites",
|
||||||
"prebuild": "npm run sprites"
|
"prebuild": "npm run sprites"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Создайте точку входа для сгенерированного API:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// assets/app-icons/index.ts
|
||||||
|
export * from './.svg-sprite/index.js'
|
||||||
|
```
|
||||||
|
|
||||||
Первый запуск:
|
Первый запуск:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -109,10 +88,11 @@ npm run sprites
|
|||||||
|
|
||||||
Пакет создаст `AppIcon`, TypeScript-типы и отдельный SVG-спрайт.
|
Пакет создаст `AppIcon`, TypeScript-типы и отдельный SVG-спрайт.
|
||||||
|
|
||||||
### 4. Используйте как обычную иконку
|
### 3. Используйте как обычную иконку
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
import { AppIcon } from '@/ui/app-icons'
|
// app/page.tsx
|
||||||
|
import { AppIcon } from '../assets/app-icons'
|
||||||
|
|
||||||
export default function SearchButton() {
|
export default function SearchButton() {
|
||||||
return (
|
return (
|
||||||
@@ -277,6 +257,7 @@ README знакомит с возможностями проекта и пока
|
|||||||
### Технические материалы
|
### Технические материалы
|
||||||
|
|
||||||
- [Индекс документации](docs/ru/README.md)
|
- [Индекс документации](docs/ru/README.md)
|
||||||
|
- [Конфигурация](docs/ru/configuration.md)
|
||||||
- [Технический справочник](docs/ru/reference/technical.md)
|
- [Технический справочник](docs/ru/reference/technical.md)
|
||||||
- [Программный API](docs/ru/reference/programmatic-api.md)
|
- [Программный API](docs/ru/reference/programmatic-api.md)
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
Для настройки выберите guide одного exact mode. Каждый guide является
|
Для настройки выберите guide одного exact mode. Каждый guide является
|
||||||
самостоятельным документом и без изменений используется в AI skills.
|
самостоятельным документом и без изменений используется в AI skills.
|
||||||
|
|
||||||
|
Общий формат JSON, JavaScript и TypeScript config-файлов описан в [руководстве по конфигурации](configuration.md).
|
||||||
|
|
||||||
## Гайды быстрого старта
|
## Гайды быстрого старта
|
||||||
|
|
||||||
| Проект | Exact mode | Guide |
|
| Проект | Exact mode | Guide |
|
||||||
@@ -20,10 +22,11 @@
|
|||||||
Все guides используют один порядок:
|
Все guides используют один порядок:
|
||||||
|
|
||||||
1. Генерация спрайта через `npx` без добавления package в проект.
|
1. Генерация спрайта через `npx` без добавления package в проект.
|
||||||
2. Необязательное подключение Viewer для дебага и превью.
|
2. Использование спрайта в приложении.
|
||||||
3. Необязательная типизация конфига через package или локальный copy-paste type.
|
3. Необязательное подключение Viewer для дебага и превью.
|
||||||
|
|
||||||
## Справочники
|
## Справочники
|
||||||
|
|
||||||
|
- [Конфигурация](configuration.md)
|
||||||
- [Технический справочник](reference/technical.md)
|
- [Технический справочник](reference/technical.md)
|
||||||
- [Программный API](reference/programmatic-api.md)
|
- [Программный API](reference/programmatic-api.md)
|
||||||
|
|||||||
99
docs/ru/configuration.md
Normal file
99
docs/ru/configuration.md
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
# Конфигурация
|
||||||
|
|
||||||
|
Каждый config-файл описывает один независимый спрайт. CLI не ищет конфиг автоматически, поэтому всегда передавайте путь явно:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx --yes @gromlab/svg-sprites path/to/svg-sprite.config.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## JSON
|
||||||
|
|
||||||
|
JSON подходит для большинства проектов и не требует локальной установки пакета:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"mode": "next@app/turbopack",
|
||||||
|
"name": "app",
|
||||||
|
"description": "Общие иконки приложения",
|
||||||
|
"input": [
|
||||||
|
"./icons",
|
||||||
|
"../../assets/icons/**/*.svg",
|
||||||
|
"!../../assets/icons/deprecated-*.svg"
|
||||||
|
],
|
||||||
|
"transform": {
|
||||||
|
"removeSize": true,
|
||||||
|
"replaceColors": true,
|
||||||
|
"addTransition": true
|
||||||
|
},
|
||||||
|
"generatedNotice": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
| Поле | По умолчанию | Назначение |
|
||||||
|
|---|---|---|
|
||||||
|
| `mode` | Нет | Exact mode, соответствующий framework и сборщику |
|
||||||
|
| `name` | Kebab-case имени каталога модуля; для `svg-sprite` и `svg-sprites` — имени родительского каталога | Имя спрайта; в modes с компонентом также задаёт имя компонента и типов |
|
||||||
|
| `description` | Нет | Описание для типов и Viewer |
|
||||||
|
| `input` | `./icons` | Каталог, SVG-файл, glob-шаблон или массив источников |
|
||||||
|
| `transform` | Все включены | Настройки подготовки SVG |
|
||||||
|
| `generatedNotice` | `true` | Вид предупреждения в generated-файлах |
|
||||||
|
|
||||||
|
Пути и glob-шаблоны в `input` считаются от каталога config-файла. Паттерн с префиксом `!` исключает совпадения.
|
||||||
|
|
||||||
|
## JavaScript
|
||||||
|
|
||||||
|
JavaScript-конфиг экспортирует обычный объект по умолчанию:
|
||||||
|
|
||||||
|
```js
|
||||||
|
export default {
|
||||||
|
mode: 'react@vite',
|
||||||
|
name: 'icons',
|
||||||
|
input: './icons',
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Передайте CLI путь к `.js`-файлу так же, как к JSON:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx --yes @gromlab/svg-sprites path/to/svg-sprite.config.js
|
||||||
|
```
|
||||||
|
|
||||||
|
## TypeScript
|
||||||
|
|
||||||
|
Для проверки конфига TypeScript установите пакет как dev dependency:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install --save-dev @gromlab/svg-sprites
|
||||||
|
```
|
||||||
|
|
||||||
|
Используйте `defineSpriteConfig`:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { defineSpriteConfig } from '@gromlab/svg-sprites'
|
||||||
|
|
||||||
|
export default defineSpriteConfig({
|
||||||
|
mode: 'react@vite',
|
||||||
|
name: 'icons',
|
||||||
|
input: './icons',
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
Или примените `satisfies` с type-only импортом:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import type { SpriteConfig } from '@gromlab/svg-sprites'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mode: 'react@vite',
|
||||||
|
name: 'icons',
|
||||||
|
input: './icons',
|
||||||
|
} satisfies SpriteConfig
|
||||||
|
```
|
||||||
|
|
||||||
|
CLI загружает `.ts`-конфиг напрямую:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx --yes @gromlab/svg-sprites path/to/svg-sprite.config.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
Полный список modes, CLI-флагов, правил именования и transform-опций находится в [техническом справочнике](reference/technical.md).
|
||||||
78
docs/ru/guides/AGENTS.md
Normal file
78
docs/ru/guides/AGENTS.md
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
# Правила гайдов быстрого старта
|
||||||
|
|
||||||
|
## Цель
|
||||||
|
|
||||||
|
Гайд должен помочь читателю как можно быстрее создать SVG-спрайт и использовать его в своём приложении.
|
||||||
|
|
||||||
|
Спрайт является главным результатом. Компоненты и другие сгенерированные файлы описываются только как средства его использования.
|
||||||
|
|
||||||
|
## Область гайда
|
||||||
|
|
||||||
|
Каждый гайд посвящён одному exact mode.
|
||||||
|
|
||||||
|
В гайд включаются только действия и особенности, относящиеся к этому mode. Нельзя переносить в него поведение других сборщиков, фреймворков или modes.
|
||||||
|
|
||||||
|
Все технические утверждения необходимо проверять по реализации соответствующего adapter.
|
||||||
|
|
||||||
|
## Структура
|
||||||
|
|
||||||
|
Гайд состоит из трёх основных частей:
|
||||||
|
|
||||||
|
1. Генерация спрайта.
|
||||||
|
2. Использование спрайта.
|
||||||
|
3. Дебаг и превью через Viewer.
|
||||||
|
|
||||||
|
Первая строка после заголовка должна объяснять, что это инструкция по быстрому созданию SVG-спрайта и для какого приложения она предназначена.
|
||||||
|
|
||||||
|
## Примеры
|
||||||
|
|
||||||
|
Все примеры внутри гайда должны составлять один последовательный сценарий.
|
||||||
|
|
||||||
|
Пути, имена, команды, импорты и названия сгенерированных API должны соответствовать друг другу и фактическому результату генерации.
|
||||||
|
|
||||||
|
Во всех гайдах используются согласованные примеры:
|
||||||
|
|
||||||
|
- исходные SVG находятся в `assets/svg-icons`;
|
||||||
|
- спрайт создаётся в `assets/app-icons`;
|
||||||
|
- конфиг записывается в JSON;
|
||||||
|
- имя спрайта в конфиге — `app`.
|
||||||
|
|
||||||
|
## Зависимости
|
||||||
|
|
||||||
|
В разделе генерации нужно явно показать ключевое преимущество: для создания и использования спрайта пакет не требуется добавлять в зависимости проекта.
|
||||||
|
|
||||||
|
Viewer описывается отдельно как необязательный инструмент разработки. Установка или подключение пакета допускается только в разделе Viewer и только способом, подходящим текущему mode.
|
||||||
|
|
||||||
|
## Содержание
|
||||||
|
|
||||||
|
Гайд должен содержать только минимальный рабочий путь:
|
||||||
|
|
||||||
|
- структуру проекта;
|
||||||
|
- конфиг;
|
||||||
|
- команду генерации;
|
||||||
|
- автоматическую генерацию перед запуском и сборкой, если она необходима;
|
||||||
|
- подключение иконки;
|
||||||
|
- базовую настройку размера и цветов;
|
||||||
|
- подключение Viewer.
|
||||||
|
|
||||||
|
Особенности mode добавляются только тогда, когда без них пример не работает или работает неправильно.
|
||||||
|
|
||||||
|
## Технический шум
|
||||||
|
|
||||||
|
Не нужно описывать внутреннее устройство генератора, сгенерированных файлов и сборщика.
|
||||||
|
|
||||||
|
Не нужно перечислять альтернативные конфигурации, дополнительные API, редкие сценарии и ограничения, не относящиеся к быстрому старту.
|
||||||
|
|
||||||
|
Подробности должны оставаться в reference-документации.
|
||||||
|
|
||||||
|
## Стиль
|
||||||
|
|
||||||
|
Писать для пользователя, а не для разработчика библиотеки.
|
||||||
|
|
||||||
|
Использовать короткие, прямые и практические формулировки.
|
||||||
|
|
||||||
|
Сначала объяснять пользу или цель шага, затем показывать действие.
|
||||||
|
|
||||||
|
Не использовать воду, рекламные формулировки и технические термины, которые не помогают выполнить инструкцию.
|
||||||
|
|
||||||
|
Не начинать документ с перечисления сгенерированных компонентов или особенностей реализации.
|
||||||
@@ -1,153 +1,108 @@
|
|||||||
# Next.js App Router с Turbopack
|
# SVG-спрайт для Next.js App Router с Turbopack
|
||||||
|
|
||||||
Это автономный quick start для exact mode key `next@app/turbopack`: generated `IconsIcon` совместим с Server Components и asset pipeline Turbopack.
|
Инструкция по быстрому созданию SVG-спрайта в приложении Next.js с App Router и Turbopack.
|
||||||
|
|
||||||
## 1. Генерация спрайта
|
## Генерация спрайта
|
||||||
|
|
||||||
Главное преимущество: генератор не нужно устанавливать и добавлять в `package.json`. `npx` временно скачивает CLI, а generated production runtime не импортирует `@gromlab/svg-sprites`.
|
Выберите папку для спрайта. В примере используется `assets/app-icons`, а исходные SVG находятся в `assets/svg-icons`.
|
||||||
|
|
||||||
```text
|
Создайте конфиг `assets/app-icons/svg-sprite.config.json`:
|
||||||
src/sprite/
|
|
||||||
├── icons/
|
|
||||||
│ ├── check.svg
|
|
||||||
│ └── warning.svg
|
|
||||||
├── index.ts
|
|
||||||
└── svg-sprite.config.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
Минимальный plain config:
|
```json
|
||||||
|
{
|
||||||
```ts
|
"mode": "next@app/turbopack",
|
||||||
export default {
|
"name": "app",
|
||||||
mode: 'next@app/turbopack',
|
"input": "../svg-icons/**/*.svg"
|
||||||
name: 'icons',
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Если `input` не указан, SVG читаются из `./icons` относительно конфига. Конфиг может быть `.ts`, `.js` с `default export` или `.json`.
|
Путь в `input` считается от папки с конфигом.
|
||||||
|
|
||||||
```bash
|
Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой:
|
||||||
npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
В CI зафиксируйте точную версию, например `@gromlab/svg-sprites@1.1.5`. Mode и команды Next должны указывать один bundler:
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts",
|
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
|
||||||
"dev": "npm run sprites && next dev --turbopack",
|
"predev": "npm run sprites",
|
||||||
"build": "npm run sprites && next build --turbopack",
|
"dev": "next dev --turbopack",
|
||||||
"start": "next start",
|
"prebuild": "npm run sprites",
|
||||||
"typecheck": "npm run sprites && tsc --noEmit"
|
"build": "next build --turbopack"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Не добавляйте одновременно `predev`/`prebuild` и явный `npm run sprites`. Generated `.svg-sprite` не коммитится. Generated локальный `.gitignore`, который исключает этот каталог, нужно добавить в Git один раз. Его `.d.ts` self-contained и не импортируют generator package.
|
## Использование спрайта
|
||||||
|
|
||||||
|
Значение `name: "app"` создаёт React-компонент `AppIcon`.
|
||||||
|
|
||||||
|
Создайте точку входа `assets/app-icons/index.ts`:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
// src/sprite/index.ts
|
|
||||||
export * from './.svg-sprite/index.js'
|
export * from './.svg-sprite/index.js'
|
||||||
```
|
```
|
||||||
|
|
||||||
Generated icon не содержит `'use client'`, поэтому его можно импортировать прямо в Server Component:
|
Используйте компонент в Server Component:
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
// app/page.tsx
|
// app/page.tsx
|
||||||
import { IconsIcon, iconsIconNames } from '../src/sprite'
|
import { AppIcon } from '../assets/app-icons'
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
return (
|
return (
|
||||||
<main>
|
<AppIcon
|
||||||
<IconsIcon
|
icon="check"
|
||||||
icon="check"
|
width={24}
|
||||||
width={24}
|
height={24}
|
||||||
height={24}
|
role="img"
|
||||||
aria-label="Готово"
|
aria-label="Готово"
|
||||||
style={{ '--icon-color-1': '#16a34a' }}
|
style={{
|
||||||
/>
|
color: '#334155',
|
||||||
<span>{iconsIconNames.length} иконок</span>
|
'--icon-color-2': '#f59e0b',
|
||||||
</main>
|
}}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Turbopack обрабатывает generated `new URL('../sprite.svg', import.meta.url).href` и выпускает внешний hashed asset. Не добавляйте Client Component boundary только ради `IconsIcon`.
|
Для `AppIcon` не нужен `'use client'`. Turbopack сам добавляет `sprite.svg` в итоговую сборку, поэтому переносить его в `public` не нужно.
|
||||||
|
|
||||||
## 2. Дебаг и превью
|
## Дебаг и превью
|
||||||
|
|
||||||
Viewer необязателен и нужен только для debug/preview:
|
Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки и устанавливается отдельно:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install --save-dev @gromlab/svg-sprites
|
npm install --save-dev @gromlab/svg-sprites
|
||||||
```
|
```
|
||||||
|
|
||||||
Viewer интерактивен, поэтому создайте для него отдельный Client Component:
|
Создайте Client Component `app/svg-sprite/SvgSpriteViewer.tsx`:
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
// app/icons-debug/sprite-viewer.tsx
|
|
||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { SpriteViewer } from '@gromlab/svg-sprites/react'
|
import { SpriteViewer } from '@gromlab/svg-sprites/react'
|
||||||
|
|
||||||
const sources = [
|
const sources = [
|
||||||
() => import('../../src/sprite/.svg-sprite/svg-sprite.manifest.js'),
|
() => import('../../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'),
|
||||||
] as const
|
] as const
|
||||||
|
|
||||||
export function AppSpriteViewer() {
|
export function SvgSpriteViewer() {
|
||||||
return <SpriteViewer sources={sources} title="Иконки проекта" />
|
return <SpriteViewer sources={sources} title="Иконки проекта" />
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Server page импортирует только эту boundary:
|
Создайте маршрут `app/svg-sprite/page.tsx`:
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
// app/icons-debug/page.tsx
|
import { notFound } from 'next/navigation'
|
||||||
import { AppSpriteViewer } from './sprite-viewer'
|
|
||||||
|
|
||||||
export default function IconsDebugPage() {
|
import { SvgSpriteViewer } from './SvgSpriteViewer'
|
||||||
return <AppSpriteViewer />
|
|
||||||
|
export default function SvgSpritePage() {
|
||||||
|
if (process.env.NODE_ENV !== 'development') notFound()
|
||||||
|
|
||||||
|
return <SvgSpriteViewer />
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Viewer не входит в production icon runtime и не нужен обычным страницам с `IconsIcon`.
|
Запустите `npm run dev` и откройте `/svg-sprite`. В production маршрут вернёт 404.
|
||||||
|
|
||||||
## 3. Типизация конфига
|
|
||||||
|
|
||||||
После локальной установки package используйте helper:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineSpriteConfig } from '@gromlab/svg-sprites'
|
|
||||||
|
|
||||||
export default defineSpriteConfig({
|
|
||||||
mode: 'next@app/turbopack',
|
|
||||||
name: 'icons',
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Возможен и type-only import `SpriteConfig` с `satisfies SpriteConfig`.
|
|
||||||
|
|
||||||
Без package добавьте локальный type в config:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
type LocalSpriteConfig = {
|
|
||||||
mode: 'next@app/turbopack'
|
|
||||||
name?: string
|
|
||||||
description?: string
|
|
||||||
input?: string | string[]
|
|
||||||
transform?: {
|
|
||||||
removeSize?: boolean
|
|
||||||
replaceColors?: boolean
|
|
||||||
addTransition?: boolean
|
|
||||||
}
|
|
||||||
generatedNotice?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
mode: 'next@app/turbopack',
|
|
||||||
name: 'icons',
|
|
||||||
} satisfies LocalSpriteConfig
|
|
||||||
```
|
|
||||||
|
|
||||||
Exact literal защищает от смешивания App Router и других bundler contracts.
|
|
||||||
|
|||||||
@@ -1,151 +1,108 @@
|
|||||||
# Next.js App Router с Webpack
|
# SVG-спрайт для Next.js App Router с Webpack
|
||||||
|
|
||||||
Это автономный quick start для exact mode key `next@app/webpack`: generated `IconsIcon` совместим с Server Components и Webpack pipeline Next.js.
|
Инструкция по быстрому созданию SVG-спрайта в приложении Next.js с App Router и Webpack.
|
||||||
|
|
||||||
## 1. Генерация спрайта
|
## Генерация спрайта
|
||||||
|
|
||||||
Главное преимущество: генератор не нужно устанавливать и добавлять в `package.json`. `npx` временно скачивает CLI, а generated production runtime не импортирует `@gromlab/svg-sprites`.
|
Выберите папку для спрайта. В примере используется `assets/app-icons`, а исходные SVG находятся в `assets/svg-icons`.
|
||||||
|
|
||||||
```text
|
Создайте конфиг `assets/app-icons/svg-sprite.config.json`:
|
||||||
src/sprite/
|
|
||||||
├── icons/
|
|
||||||
│ ├── check.svg
|
|
||||||
│ └── warning.svg
|
|
||||||
├── index.ts
|
|
||||||
└── svg-sprite.config.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
Минимальный plain config:
|
```json
|
||||||
|
{
|
||||||
```ts
|
"mode": "next@app/webpack",
|
||||||
export default {
|
"name": "app",
|
||||||
mode: 'next@app/webpack',
|
"input": "../svg-icons/**/*.svg"
|
||||||
name: 'icons',
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Если `input` не указан, SVG читаются из `./icons` относительно конфига. Также поддерживаются `.js` с `default export` и `.json`.
|
Путь в `input` считается от папки с конфигом.
|
||||||
|
|
||||||
```bash
|
Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой:
|
||||||
npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
В CI замените `latest` точной версией, например `@gromlab/svg-sprites@1.1.5`. Exact Next commands для этого mode:
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts",
|
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
|
||||||
"dev": "npm run sprites && next dev --webpack",
|
"predev": "npm run sprites",
|
||||||
"build": "npm run sprites && next build --webpack",
|
"dev": "next dev --webpack",
|
||||||
"start": "next start",
|
"prebuild": "npm run sprites",
|
||||||
"typecheck": "npm run sprites && tsc --noEmit"
|
"build": "next build --webpack"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Не сочетайте явный `npm run sprites` с `predev` или `prebuild`. `.svg-sprite` generated и не коммитится. Generated локальный `.gitignore`, который исключает этот каталог, нужно добавить в Git один раз. Generated declarations self-contained и не зависят от `@gromlab/svg-sprites`.
|
## Использование спрайта
|
||||||
|
|
||||||
|
Значение `name: "app"` создаёт React-компонент `AppIcon`.
|
||||||
|
|
||||||
|
Создайте точку входа `assets/app-icons/index.ts`:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
// src/sprite/index.ts
|
|
||||||
export * from './.svg-sprite/index.js'
|
export * from './.svg-sprite/index.js'
|
||||||
```
|
```
|
||||||
|
|
||||||
Production usage в Server Component:
|
Используйте компонент в Server Component:
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
// app/page.tsx
|
// app/page.tsx
|
||||||
import { IconsIcon, iconsIconNames } from '../src/sprite'
|
import { AppIcon } from '../assets/app-icons'
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
return (
|
return (
|
||||||
<main>
|
<AppIcon
|
||||||
<IconsIcon
|
icon="check"
|
||||||
icon="check"
|
width={24}
|
||||||
width={24}
|
height={24}
|
||||||
height={24}
|
role="img"
|
||||||
aria-label="Готово"
|
aria-label="Готово"
|
||||||
style={{ '--icon-color-1': '#16a34a' }}
|
style={{
|
||||||
/>
|
color: '#334155',
|
||||||
<span>{iconsIconNames.length} иконок</span>
|
'--icon-color-2': '#f59e0b',
|
||||||
</main>
|
}}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Generated component не содержит `'use client'`. Next Webpack обрабатывает `new URL('../sprite.svg', import.meta.url).href` и публикует отдельный SVG asset; не переписывайте этот URL и не переносите sprite в `public` вручную.
|
Для `AppIcon` не нужен `'use client'`. Next.js сам добавляет `sprite.svg` в итоговую сборку, поэтому переносить его в `public` не нужно.
|
||||||
|
|
||||||
## 2. Дебаг и превью
|
## Дебаг и превью
|
||||||
|
|
||||||
Viewer необязателен. Для debug/preview установите package:
|
Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки и устанавливается отдельно:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install --save-dev @gromlab/svg-sprites
|
npm install --save-dev @gromlab/svg-sprites
|
||||||
```
|
```
|
||||||
|
|
||||||
App Router требует отдельную Client Component boundary для Viewer:
|
Создайте Client Component `app/svg-sprite/SvgSpriteViewer.tsx`:
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
// app/icons-debug/sprite-viewer.tsx
|
|
||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { SpriteViewer } from '@gromlab/svg-sprites/react'
|
import { SpriteViewer } from '@gromlab/svg-sprites/react'
|
||||||
|
|
||||||
const sources = [
|
const sources = [
|
||||||
() => import('../../src/sprite/.svg-sprite/svg-sprite.manifest.js'),
|
() => import('../../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'),
|
||||||
] as const
|
] as const
|
||||||
|
|
||||||
export function AppSpriteViewer() {
|
export function SvgSpriteViewer() {
|
||||||
return <SpriteViewer sources={sources} title="Иконки проекта" />
|
return <SpriteViewer sources={sources} title="Иконки проекта" />
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Создайте маршрут `app/svg-sprite/page.tsx`:
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
// app/icons-debug/page.tsx
|
import { notFound } from 'next/navigation'
|
||||||
import { AppSpriteViewer } from './sprite-viewer'
|
|
||||||
|
|
||||||
export default function IconsDebugPage() {
|
import { SvgSpriteViewer } from './SvgSpriteViewer'
|
||||||
return <AppSpriteViewer />
|
|
||||||
|
export default function SvgSpritePage() {
|
||||||
|
if (process.env.NODE_ENV !== 'development') notFound()
|
||||||
|
|
||||||
|
return <SvgSpriteViewer />
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Статический loader позволяет Webpack связать manifest и emitted SVG. Viewer не входит в production runtime `IconsIcon`.
|
Запустите `npm run dev` и откройте `/svg-sprite`. В production маршрут вернёт 404.
|
||||||
|
|
||||||
## 3. Типизация конфига
|
|
||||||
|
|
||||||
При локально установленном package используйте helper:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineSpriteConfig } from '@gromlab/svg-sprites'
|
|
||||||
|
|
||||||
export default defineSpriteConfig({
|
|
||||||
mode: 'next@app/webpack',
|
|
||||||
name: 'icons',
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Другой package-вариант: type-only import `SpriteConfig` и `satisfies SpriteConfig`.
|
|
||||||
|
|
||||||
Без package вставьте локальный type прямо в config:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
type LocalSpriteConfig = {
|
|
||||||
mode: 'next@app/webpack'
|
|
||||||
name?: string
|
|
||||||
description?: string
|
|
||||||
input?: string | string[]
|
|
||||||
transform?: {
|
|
||||||
removeSize?: boolean
|
|
||||||
replaceColors?: boolean
|
|
||||||
addTransition?: boolean
|
|
||||||
}
|
|
||||||
generatedNotice?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
mode: 'next@app/webpack',
|
|
||||||
name: 'icons',
|
|
||||||
} satisfies LocalSpriteConfig
|
|
||||||
```
|
|
||||||
|
|
||||||
Локальный exact literal исключает случайную генерацию Turbopack или Pages Router output.
|
|
||||||
|
|||||||
@@ -1,140 +1,98 @@
|
|||||||
# Next.js Pages Router с Turbopack
|
# SVG-спрайт для Next.js Pages Router с Turbopack
|
||||||
|
|
||||||
Это автономный quick start для exact mode key `next@pages/turbopack`: generated `IconsIcon` работает при SSR, SSG и клиентских переходах Pages Router.
|
Инструкция по быстрому созданию SVG-спрайта в приложении Next.js с Pages Router и Turbopack.
|
||||||
|
|
||||||
## 1. Генерация спрайта
|
## Генерация спрайта
|
||||||
|
|
||||||
Главное преимущество: генератор не нужно устанавливать и добавлять в `package.json`. `npx` временно скачивает CLI, а generated production runtime не импортирует `@gromlab/svg-sprites`.
|
Выберите папку для спрайта. В примере используется `assets/app-icons`, а исходные SVG находятся в `assets/svg-icons`.
|
||||||
|
|
||||||
```text
|
Создайте конфиг `assets/app-icons/svg-sprite.config.json`:
|
||||||
src/sprite/
|
|
||||||
├── icons/
|
|
||||||
│ ├── check.svg
|
|
||||||
│ └── warning.svg
|
|
||||||
├── index.ts
|
|
||||||
└── svg-sprite.config.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
Минимальный plain config:
|
```json
|
||||||
|
{
|
||||||
```ts
|
"mode": "next@pages/turbopack",
|
||||||
export default {
|
"name": "app",
|
||||||
mode: 'next@pages/turbopack',
|
"input": "../svg-icons/**/*.svg"
|
||||||
name: 'icons',
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Если `input` не указан, SVG читаются из `./icons` относительно конфига. Конфиг также может быть `.js` с `default export` или `.json`.
|
Путь в `input` считается от папки с конфигом.
|
||||||
|
|
||||||
```bash
|
Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой:
|
||||||
npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
Для CI зафиксируйте точную версию, например `@gromlab/svg-sprites@1.1.5`. Exact commands должны сохранять Turbopack и для dev, и для production build:
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts",
|
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
|
||||||
"dev": "npm run sprites && next dev --turbopack",
|
"predev": "npm run sprites",
|
||||||
"build": "npm run sprites && next build --turbopack",
|
"dev": "next dev --turbopack",
|
||||||
"start": "next start",
|
"prebuild": "npm run sprites",
|
||||||
"typecheck": "npm run sprites && tsc --noEmit"
|
"build": "next build --turbopack"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Не добавляйте `predev`/`prebuild`, если scripts уже явно вызывают `npm run sprites`. Generated `.svg-sprite` не коммитится. Generated локальный `.gitignore`, который исключает этот каталог, нужно добавить в Git один раз. Generated `.d.ts` self-contained и не импортируют generator package.
|
## Использование спрайта
|
||||||
|
|
||||||
|
Значение `name: "app"` создаёт React-компонент `AppIcon`.
|
||||||
|
|
||||||
|
Создайте точку входа `assets/app-icons/index.ts`:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
// src/sprite/index.ts
|
|
||||||
export * from './.svg-sprite/index.js'
|
export * from './.svg-sprite/index.js'
|
||||||
```
|
```
|
||||||
|
|
||||||
Production usage на обычной page:
|
Используйте компонент на странице:
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
// pages/index.tsx
|
// pages/index.tsx
|
||||||
import { IconsIcon, iconsIconNames } from '../src/sprite'
|
import { AppIcon } from '../assets/app-icons'
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
return (
|
return (
|
||||||
<main>
|
<AppIcon
|
||||||
<IconsIcon
|
icon="check"
|
||||||
icon="check"
|
width={24}
|
||||||
width={24}
|
height={24}
|
||||||
height={24}
|
role="img"
|
||||||
aria-label="Готово"
|
aria-label="Готово"
|
||||||
style={{ '--icon-color-1': '#16a34a' }}
|
style={{
|
||||||
/>
|
color: '#334155',
|
||||||
<span>{iconsIconNames.length} иконок</span>
|
'--icon-color-2': '#f59e0b',
|
||||||
</main>
|
}}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Компонент одинаково работает с `getServerSideProps`, `getStaticProps` и client navigation. Turbopack разрешает generated `new URL('../sprite.svg', import.meta.url).href` в отдельный hashed asset.
|
Компонент работает с SSR, SSG и клиентскими переходами. Turbopack сам добавляет `sprite.svg` в итоговую сборку, поэтому переносить его в `public` не нужно.
|
||||||
|
|
||||||
## 2. Дебаг и превью
|
## Дебаг и превью
|
||||||
|
|
||||||
Viewer необязателен и нужен только для debug/preview:
|
Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки и устанавливается отдельно:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install --save-dev @gromlab/svg-sprites
|
npm install --save-dev @gromlab/svg-sprites
|
||||||
```
|
```
|
||||||
|
|
||||||
В Pages Router Viewer можно использовать прямо в page, без отдельной App Router Client Component boundary:
|
Создайте страницу `pages/svg-sprite.tsx`:
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
// pages/icons-debug.tsx
|
import type { GetStaticProps } from 'next'
|
||||||
import { SpriteViewer } from '@gromlab/svg-sprites/react'
|
import { SpriteViewer } from '@gromlab/svg-sprites/react'
|
||||||
|
|
||||||
const sources = [
|
const sources = [
|
||||||
() => import('../src/sprite/.svg-sprite/svg-sprite.manifest.js'),
|
() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'),
|
||||||
] as const
|
] as const
|
||||||
|
|
||||||
export default function IconsDebugPage() {
|
export default function SvgSpritePage() {
|
||||||
return <SpriteViewer sources={sources} title="Иконки проекта" />
|
return <SpriteViewer sources={sources} title="Иконки проекта" />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getStaticProps: GetStaticProps = () =>
|
||||||
|
process.env.NODE_ENV === 'development'
|
||||||
|
? { props: {} }
|
||||||
|
: { notFound: true }
|
||||||
```
|
```
|
||||||
|
|
||||||
Оставляйте эту page только во внутреннем debug-разделе. Viewer не входит в production icon runtime `IconsIcon`.
|
Запустите `npm run dev` и откройте `/svg-sprite`. В production маршрут вернёт 404.
|
||||||
|
|
||||||
## 3. Типизация конфига
|
|
||||||
|
|
||||||
Если package установлен локально, используйте helper:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineSpriteConfig } from '@gromlab/svg-sprites'
|
|
||||||
|
|
||||||
export default defineSpriteConfig({
|
|
||||||
mode: 'next@pages/turbopack',
|
|
||||||
name: 'icons',
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Альтернатива: type-only import `SpriteConfig` и объект `satisfies SpriteConfig`.
|
|
||||||
|
|
||||||
Без package добавьте copy-paste type в config:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
type LocalSpriteConfig = {
|
|
||||||
mode: 'next@pages/turbopack'
|
|
||||||
name?: string
|
|
||||||
description?: string
|
|
||||||
input?: string | string[]
|
|
||||||
transform?: {
|
|
||||||
removeSize?: boolean
|
|
||||||
replaceColors?: boolean
|
|
||||||
addTransition?: boolean
|
|
||||||
}
|
|
||||||
generatedNotice?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
mode: 'next@pages/turbopack',
|
|
||||||
name: 'icons',
|
|
||||||
} satisfies LocalSpriteConfig
|
|
||||||
```
|
|
||||||
|
|
||||||
Exact literal не позволяет незаметно смешать Pages Router с App Router или Webpack output.
|
|
||||||
|
|||||||
@@ -1,140 +1,98 @@
|
|||||||
# Next.js Pages Router с Webpack
|
# SVG-спрайт для Next.js Pages Router с Webpack
|
||||||
|
|
||||||
Это автономный quick start для exact mode key `next@pages/webpack`: generated `IconsIcon` работает в Pages Router и публикует SVG через Webpack pipeline Next.js.
|
Инструкция по быстрому созданию SVG-спрайта в приложении Next.js с Pages Router и Webpack.
|
||||||
|
|
||||||
## 1. Генерация спрайта
|
## Генерация спрайта
|
||||||
|
|
||||||
Главное преимущество: генератор не нужно устанавливать и добавлять в `package.json`. `npx` временно скачивает CLI, а generated production runtime не импортирует `@gromlab/svg-sprites`.
|
Выберите папку для спрайта. В примере используется `assets/app-icons`, а исходные SVG находятся в `assets/svg-icons`.
|
||||||
|
|
||||||
```text
|
Создайте конфиг `assets/app-icons/svg-sprite.config.json`:
|
||||||
src/sprite/
|
|
||||||
├── icons/
|
|
||||||
│ ├── check.svg
|
|
||||||
│ └── warning.svg
|
|
||||||
├── index.ts
|
|
||||||
└── svg-sprite.config.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
Минимальный plain config:
|
```json
|
||||||
|
{
|
||||||
```ts
|
"mode": "next@pages/webpack",
|
||||||
export default {
|
"name": "app",
|
||||||
mode: 'next@pages/webpack',
|
"input": "../svg-icons/**/*.svg"
|
||||||
name: 'icons',
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Если `input` не указан, SVG читаются из `./icons` относительно конфига. Поддерживаются `.ts`, `.js` с `default export` и `.json` configs.
|
Путь в `input` считается от папки с конфигом.
|
||||||
|
|
||||||
```bash
|
Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой:
|
||||||
npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
В CI замените `latest` на точную проверенную версию, например `@gromlab/svg-sprites@1.1.5`. Exact Next commands для Webpack:
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts",
|
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
|
||||||
"dev": "npm run sprites && next dev --webpack",
|
"predev": "npm run sprites",
|
||||||
"build": "npm run sprites && next build --webpack",
|
"dev": "next dev --webpack",
|
||||||
"start": "next start",
|
"prebuild": "npm run sprites",
|
||||||
"typecheck": "npm run sprites && tsc --noEmit"
|
"build": "next build --webpack"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Не дублируйте эти вызовы через `predev`, `prebuild` или `pretypecheck`. `.svg-sprite` generated и не коммитится. Generated локальный `.gitignore`, который исключает этот каталог, нужно добавить в Git один раз. Generated declarations self-contained и не требуют `@gromlab/svg-sprites`.
|
## Использование спрайта
|
||||||
|
|
||||||
|
Значение `name: "app"` создаёт React-компонент `AppIcon`.
|
||||||
|
|
||||||
|
Создайте точку входа `assets/app-icons/index.ts`:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
// src/sprite/index.ts
|
|
||||||
export * from './.svg-sprite/index.js'
|
export * from './.svg-sprite/index.js'
|
||||||
```
|
```
|
||||||
|
|
||||||
Production usage:
|
Используйте компонент на странице:
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
// pages/index.tsx
|
// pages/index.tsx
|
||||||
import { IconsIcon, iconsIconNames } from '../src/sprite'
|
import { AppIcon } from '../assets/app-icons'
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
return (
|
return (
|
||||||
<main>
|
<AppIcon
|
||||||
<IconsIcon
|
icon="check"
|
||||||
icon="check"
|
width={24}
|
||||||
width={24}
|
height={24}
|
||||||
height={24}
|
role="img"
|
||||||
aria-label="Готово"
|
aria-label="Готово"
|
||||||
style={{ '--icon-color-1': '#16a34a' }}
|
style={{
|
||||||
/>
|
color: '#334155',
|
||||||
<span>{iconsIconNames.length} иконок</span>
|
'--icon-color-2': '#f59e0b',
|
||||||
</main>
|
}}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Компонент поддерживает SSR, SSG и клиентские переходы. Next Webpack преобразует generated `new URL('../sprite.svg', import.meta.url).href` во внешний hashed asset; не конструируйте URL спрайта вручную.
|
Компонент работает с SSR, SSG и клиентскими переходами. Next.js сам добавляет `sprite.svg` в итоговую сборку, поэтому переносить его в `public` не нужно.
|
||||||
|
|
||||||
## 2. Дебаг и превью
|
## Дебаг и превью
|
||||||
|
|
||||||
Viewer необязателен. Устанавливайте package только для debug/preview:
|
Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки и устанавливается отдельно:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install --save-dev @gromlab/svg-sprites
|
npm install --save-dev @gromlab/svg-sprites
|
||||||
```
|
```
|
||||||
|
|
||||||
Pages Router позволяет разместить Viewer непосредственно в page без отдельной App Router boundary:
|
Создайте страницу `pages/svg-sprite.tsx`:
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
// pages/icons-debug.tsx
|
import type { GetStaticProps } from 'next'
|
||||||
import { SpriteViewer } from '@gromlab/svg-sprites/react'
|
import { SpriteViewer } from '@gromlab/svg-sprites/react'
|
||||||
|
|
||||||
const sources = [
|
const sources = [
|
||||||
() => import('../src/sprite/.svg-sprite/svg-sprite.manifest.js'),
|
() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'),
|
||||||
] as const
|
] as const
|
||||||
|
|
||||||
export default function IconsDebugPage() {
|
export default function SvgSpritePage() {
|
||||||
return <SpriteViewer sources={sources} title="Иконки проекта" />
|
return <SpriteViewer sources={sources} title="Иконки проекта" />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getStaticProps: GetStaticProps = () =>
|
||||||
|
process.env.NODE_ENV === 'development'
|
||||||
|
? { props: {} }
|
||||||
|
: { notFound: true }
|
||||||
```
|
```
|
||||||
|
|
||||||
Статический loader даёт Webpack точный manifest module и связанный SVG asset. Не импортируйте Viewer из production pages, если preview там не нужен; runtime `IconsIcon` от него независим.
|
Запустите `npm run dev` и откройте `/svg-sprite`. В production маршрут вернёт 404.
|
||||||
|
|
||||||
## 3. Типизация конфига
|
|
||||||
|
|
||||||
После локальной установки package доступен helper:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineSpriteConfig } from '@gromlab/svg-sprites'
|
|
||||||
|
|
||||||
export default defineSpriteConfig({
|
|
||||||
mode: 'next@pages/webpack',
|
|
||||||
name: 'icons',
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Также можно применить `satisfies SpriteConfig` с type-only импортом `SpriteConfig`.
|
|
||||||
|
|
||||||
Без package вставьте локальный type прямо в config:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
type LocalSpriteConfig = {
|
|
||||||
mode: 'next@pages/webpack'
|
|
||||||
name?: string
|
|
||||||
description?: string
|
|
||||||
input?: string | string[]
|
|
||||||
transform?: {
|
|
||||||
removeSize?: boolean
|
|
||||||
replaceColors?: boolean
|
|
||||||
addTransition?: boolean
|
|
||||||
}
|
|
||||||
generatedNotice?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
mode: 'next@pages/webpack',
|
|
||||||
name: 'icons',
|
|
||||||
} satisfies LocalSpriteConfig
|
|
||||||
```
|
|
||||||
|
|
||||||
Такой exact literal выявляет ошибочный выбор App Router или Turbopack ещё при проверке config.
|
|
||||||
|
|||||||
@@ -1,141 +1,115 @@
|
|||||||
# React-компонент для Vite
|
# SVG-спрайт для React на Vite
|
||||||
|
|
||||||
Это автономный quick start для exact mode key `react@vite`: генератор создаёт типизированный `IconsIcon`, а Vite публикует отдельный SVG asset.
|
Инструкция по быстрому созданию SVG-спрайта в React-приложении на Vite.
|
||||||
|
|
||||||
## 1. Генерация спрайта
|
## Генерация спрайта
|
||||||
|
|
||||||
Главное преимущество: генератор не нужно устанавливать и добавлять в `package.json`. `npx` временно скачивает CLI, а generated production runtime не импортирует `@gromlab/svg-sprites`.
|
Выберите папку для спрайта. В примере используется `assets/app-icons`, а исходные SVG находятся в `assets/svg-icons`.
|
||||||
|
|
||||||
```text
|
Создайте конфиг `assets/app-icons/svg-sprite.config.json`:
|
||||||
src/sprite/
|
|
||||||
├── icons/
|
|
||||||
│ ├── check.svg
|
|
||||||
│ └── warning.svg
|
|
||||||
├── index.ts
|
|
||||||
└── svg-sprite.config.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
Минимальный plain config:
|
```json
|
||||||
|
{
|
||||||
```ts
|
"mode": "react@vite",
|
||||||
export default {
|
"name": "app",
|
||||||
mode: 'react@vite',
|
"input": "../svg-icons/**/*.svg"
|
||||||
name: 'icons',
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Если `input` не указан, SVG читаются из `./icons` относительно конфига. Вместо `.ts` можно использовать `.js` с `default export` или `.json`.
|
Путь в `input` считается от папки с конфигом.
|
||||||
|
|
||||||
```bash
|
Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой:
|
||||||
npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
В CI замените `latest` на точную версию, например `@gromlab/svg-sprites@1.1.5`. Exact Vite commands:
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts",
|
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
|
||||||
"dev": "npm run sprites && vite",
|
"predev": "npm run sprites",
|
||||||
"build": "npm run sprites && tsc --noEmit && vite build",
|
"dev": "vite",
|
||||||
"typecheck": "npm run sprites && tsc --noEmit"
|
"prebuild": "npm run sprites",
|
||||||
|
"build": "tsc --noEmit && vite build"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Не добавляйте `predev`, `prebuild` или `pretypecheck`, если соответствующие scripts уже явно запускают `npm run sprites`. Generated `.svg-sprite` не коммитится. Generated локальный `.gitignore`, который исключает этот каталог, нужно добавить в Git один раз. Generated declarations self-contained: они описывают компонент и manifest без импорта `@gromlab/svg-sprites`.
|
## Использование спрайта
|
||||||
|
|
||||||
Пользовательский barrel возвращает generated API:
|
Значение `name: "app"` создаёт React-компонент `AppIcon`.
|
||||||
|
|
||||||
|
Создайте точку входа `assets/app-icons/index.ts`:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
// src/sprite/index.ts
|
|
||||||
export * from './.svg-sprite/index.js'
|
export * from './.svg-sprite/index.js'
|
||||||
```
|
```
|
||||||
|
|
||||||
Production usage:
|
Используйте компонент в приложении:
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
import { IconsIcon, iconsIconNames } from './sprite'
|
import { AppIcon } from '../assets/app-icons'
|
||||||
|
|
||||||
export function SaveButton() {
|
export function SaveIcon() {
|
||||||
return (
|
return (
|
||||||
<button type="button">
|
<AppIcon
|
||||||
<IconsIcon
|
icon="check"
|
||||||
icon="check"
|
width={24}
|
||||||
width={24}
|
height={24}
|
||||||
height={24}
|
role="img"
|
||||||
aria-hidden="true"
|
aria-label="Готово"
|
||||||
style={{ '--icon-color-1': '#16a34a' }}
|
style={{
|
||||||
/>
|
color: '#334155',
|
||||||
Сохранить
|
'--icon-color-2': '#f59e0b',
|
||||||
</button>
|
}}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(iconsIconNames)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Prop `icon` является union имён исходных файлов. Vite автоматически обрабатывает generated CSS Module и импорт `sprite.svg?no-inline`; query запрещает inline и заставляет Vite выпустить отдельный hashed SVG asset. Если TypeScript не знает Vite asset imports, добавьте `/// <reference types="vite/client" />` в `src/vite-env.d.ts`.
|
Свойство `icon` принимает имена исходных SVG без расширения. Монохромная иконка наследует `color`, а цвета многоцветной переопределяются через `--icon-color-N`.
|
||||||
|
|
||||||
## 2. Дебаг и превью
|
Vite сам подключает стили компонента и добавляет `sprite.svg` в итоговую сборку.
|
||||||
|
|
||||||
Viewer необязателен и нужен только для debug/preview. Установите package отдельно:
|
## Дебаг и превью
|
||||||
|
|
||||||
|
Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки и устанавливается отдельно:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install --save-dev @gromlab/svg-sprites
|
npm install --save-dev @gromlab/svg-sprites
|
||||||
```
|
```
|
||||||
|
|
||||||
Используйте React bridge со статическим массивом loaders:
|
Создайте `svg-sprite.html` в корне проекта:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Иконки проекта</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- React-корень Viewer для дебага и превью SVG-спрайта -->
|
||||||
|
<div id="svg-sprite-viewer"></div>
|
||||||
|
|
||||||
|
<!-- Подключение создаваемого ниже скрипта дебаггера -->
|
||||||
|
<script type="module" src="/src/svg-sprite-debug.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
Создайте `src/svg-sprite-debug.tsx`:
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
|
import { createRoot } from 'react-dom/client'
|
||||||
import { SpriteViewer } from '@gromlab/svg-sprites/react'
|
import { SpriteViewer } from '@gromlab/svg-sprites/react'
|
||||||
|
|
||||||
const sources = [
|
const sources = [
|
||||||
() => import('./sprite/.svg-sprite/svg-sprite.manifest.js'),
|
() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'),
|
||||||
] as const
|
] as const
|
||||||
|
|
||||||
export function IconsDebugPage() {
|
createRoot(document.getElementById('svg-sprite-viewer')!).render(
|
||||||
return <SpriteViewer sources={sources} title="Иконки проекта" />
|
<SpriteViewer sources={sources} title="Иконки проекта" />,
|
||||||
}
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
Строковый путь в `import()` должен указывать на generated JS manifest. Держите страницу за debug-маршрутом; `SpriteViewer` не входит в production runtime `IconsIcon`.
|
Запустите `npm run dev` и откройте `/svg-sprite.html`.
|
||||||
|
|
||||||
## 3. Типизация конфига
|
Стандартная production-сборка Vite использует только `index.html` и не включает страницу Viewer.
|
||||||
|
|
||||||
Если package установлен локально, используйте helper:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineSpriteConfig } from '@gromlab/svg-sprites'
|
|
||||||
|
|
||||||
export default defineSpriteConfig({
|
|
||||||
mode: 'react@vite',
|
|
||||||
name: 'icons',
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Также можно импортировать `SpriteConfig` только как type и написать объект `satisfies SpriteConfig`.
|
|
||||||
|
|
||||||
Без package добавьте локальный type прямо в config:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
type LocalSpriteConfig = {
|
|
||||||
mode: 'react@vite'
|
|
||||||
name?: string
|
|
||||||
description?: string
|
|
||||||
input?: string | string[]
|
|
||||||
transform?: {
|
|
||||||
removeSize?: boolean
|
|
||||||
replaceColors?: boolean
|
|
||||||
addTransition?: boolean
|
|
||||||
}
|
|
||||||
generatedNotice?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
mode: 'react@vite',
|
|
||||||
name: 'icons',
|
|
||||||
} satisfies LocalSpriteConfig
|
|
||||||
```
|
|
||||||
|
|
||||||
Локальный type проверяет только этот exact mode и не создаёт runtime-зависимость.
|
|
||||||
|
|||||||
@@ -1,141 +1,126 @@
|
|||||||
# React-компонент для Webpack 5
|
# SVG-спрайт для React на Webpack 5
|
||||||
|
|
||||||
Это автономный quick start для exact mode key `react@webpack`: generated `IconsIcon` использует Webpack 5 Asset Modules и CSS Modules.
|
Инструкция по быстрому созданию SVG-спрайта в React-приложении на Webpack 5.
|
||||||
|
|
||||||
## 1. Генерация спрайта
|
## Генерация спрайта
|
||||||
|
|
||||||
Главное преимущество: генератор не нужно устанавливать и добавлять в `package.json`. `npx` временно скачивает CLI, а generated production runtime не импортирует `@gromlab/svg-sprites`.
|
Выберите папку для спрайта. В примере используется `assets/app-icons`, а исходные SVG находятся в `assets/svg-icons`.
|
||||||
|
|
||||||
```text
|
Создайте конфиг `assets/app-icons/svg-sprite.config.json`:
|
||||||
src/sprite/
|
|
||||||
├── icons/
|
|
||||||
│ ├── check.svg
|
|
||||||
│ └── warning.svg
|
|
||||||
├── index.ts
|
|
||||||
└── svg-sprite.config.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
Минимальный plain config:
|
```json
|
||||||
|
{
|
||||||
```ts
|
"mode": "react@webpack",
|
||||||
export default {
|
"name": "app",
|
||||||
mode: 'react@webpack',
|
"input": "../svg-icons/**/*.svg"
|
||||||
name: 'icons',
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Если `input` не указан, SVG читаются из `./icons` относительно конфига. Поддерживаются `.ts`, `.js` с `default export` и `.json` configs.
|
Путь в `input` считается от папки с конфигом.
|
||||||
|
|
||||||
```bash
|
Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой:
|
||||||
npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
В CI замените `latest` на точную версию, например `@gromlab/svg-sprites@1.1.5`. Exact Webpack commands:
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts",
|
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
|
||||||
"dev": "npm run sprites && webpack serve --mode development",
|
"predev": "npm run sprites",
|
||||||
"build": "npm run sprites && webpack --mode production",
|
"dev": "webpack serve --mode development",
|
||||||
"typecheck": "npm run sprites && tsc --noEmit"
|
"prebuild": "npm run sprites",
|
||||||
|
"build": "webpack --mode production"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Не сочетайте эти явные вызовы с `predev`/`prebuild`: иначе генерация задублируется. `.svg-sprite` не коммитится. Generated локальный `.gitignore`, который исключает этот каталог, нужно добавить в Git один раз. Generated `.d.ts` self-contained и не импортируют generator package.
|
## Использование спрайта
|
||||||
|
|
||||||
|
Значение `name: "app"` создаёт React-компонент `AppIcon`.
|
||||||
|
|
||||||
|
Создайте точку входа `assets/app-icons/index.ts`:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
// src/sprite/index.ts
|
|
||||||
export * from './.svg-sprite/index.js'
|
export * from './.svg-sprite/index.js'
|
||||||
```
|
```
|
||||||
|
|
||||||
Production usage:
|
Используйте компонент в приложении:
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
import { IconsIcon, iconsIconNames } from './sprite'
|
import { AppIcon } from '../assets/app-icons'
|
||||||
|
|
||||||
export function SaveButton() {
|
export function SaveIcon() {
|
||||||
return (
|
return (
|
||||||
<button type="button">
|
<AppIcon
|
||||||
<IconsIcon
|
icon="check"
|
||||||
icon="check"
|
width={24}
|
||||||
width={24}
|
height={24}
|
||||||
height={24}
|
role="img"
|
||||||
aria-hidden="true"
|
aria-label="Готово"
|
||||||
style={{ '--icon-color-1': '#16a34a' }}
|
style={{
|
||||||
/>
|
color: '#334155',
|
||||||
Сохранить
|
'--icon-color-2': '#f59e0b',
|
||||||
</button>
|
}}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(iconsIconNames)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Generated component получает URL через `new URL('../sprite.svg', import.meta.url).href`. Webpack 5 должен обработать SVG как Asset Module. Исключите generated `sprite.svg` из `@svgr/webpack`, inline/raw loaders и других общих SVG rules либо задайте для него `type: 'asset/resource'`.
|
Свойство `icon` принимает имена исходных SVG без расширения. Монохромная иконка наследует `color`, а цвета многоцветной переопределяются через `--icon-color-N`.
|
||||||
|
|
||||||
Компонент импортирует `react-component.module.css`. Webpack config должен обрабатывать `*.module.css` через `css-loader` с CSS Modules и `style-loader` или `MiniCssExtractPlugin`. Для TypeScript при необходимости добавьте декларацию `declare module '*.module.css'`.
|
Компонент использует CSS Modules. Если проект ещё не обрабатывает их с default export, добавьте правило в `webpack.config.js`:
|
||||||
|
|
||||||
## 2. Дебаг и превью
|
```js
|
||||||
|
{
|
||||||
|
test: /\.module\.css$/i,
|
||||||
|
use: [
|
||||||
|
'style-loader',
|
||||||
|
{
|
||||||
|
loader: 'css-loader',
|
||||||
|
options: { modules: { namedExport: false } },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Viewer необязателен. Устанавливайте package только для debug/preview:
|
Webpack 5 сам добавляет `sprite.svg` в итоговую сборку.
|
||||||
|
|
||||||
|
## Дебаг и превью
|
||||||
|
|
||||||
|
Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки.
|
||||||
|
|
||||||
|
Установите Viewer:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install --save-dev @gromlab/svg-sprites
|
npm install --save-dev @gromlab/svg-sprites
|
||||||
```
|
```
|
||||||
|
|
||||||
Webpack не использует `import.meta.glob`; передайте статический loader:
|
Создайте entry `src/svg-sprite-debug.tsx`:
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
|
import { createRoot } from 'react-dom/client'
|
||||||
import { SpriteViewer } from '@gromlab/svg-sprites/react'
|
import { SpriteViewer } from '@gromlab/svg-sprites/react'
|
||||||
|
|
||||||
const sources = [
|
const sources = [
|
||||||
() => import('./sprite/.svg-sprite/svg-sprite.manifest.js'),
|
() => import('../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'),
|
||||||
] as const
|
] as const
|
||||||
|
|
||||||
export function IconsDebugPage() {
|
const container = document.createElement('div')
|
||||||
return <SpriteViewer sources={sources} title="Иконки проекта" />
|
document.body.append(container)
|
||||||
}
|
|
||||||
|
createRoot(container).render(
|
||||||
|
<SpriteViewer sources={sources} title="Иконки проекта" />,
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
Webpack создаст chunk manifest и разрешит его SVG через тот же Asset Modules pipeline. Viewer держите только в debug route; production `IconsIcon` от него не зависит.
|
Добавьте скрипт к основному entry только в development-режиме. Сохраните остальные настройки `webpack.config.js`:
|
||||||
|
|
||||||
## 3. Типизация конфига
|
```js
|
||||||
|
export default (_env, argv) => ({
|
||||||
С локально установленным package доступен helper:
|
// Остальные настройки Webpack.
|
||||||
|
entry: [
|
||||||
```ts
|
'./src/main.tsx',
|
||||||
import { defineSpriteConfig } from '@gromlab/svg-sprites'
|
...(argv.mode === 'development' ? ['./src/svg-sprite-debug.tsx'] : []),
|
||||||
|
],
|
||||||
export default defineSpriteConfig({
|
|
||||||
mode: 'react@webpack',
|
|
||||||
name: 'icons',
|
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
Эквивалентная проверка: type-only import `SpriteConfig` и `satisfies SpriteConfig`.
|
Запустите `npm run dev`. Viewer появится на основной странице приложения и не попадёт в production-сборку.
|
||||||
|
|
||||||
Без package используйте copy-paste type в config:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
type LocalSpriteConfig = {
|
|
||||||
mode: 'react@webpack'
|
|
||||||
name?: string
|
|
||||||
description?: string
|
|
||||||
input?: string | string[]
|
|
||||||
transform?: {
|
|
||||||
removeSize?: boolean
|
|
||||||
replaceColors?: boolean
|
|
||||||
addTransition?: boolean
|
|
||||||
}
|
|
||||||
generatedNotice?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
mode: 'react@webpack',
|
|
||||||
name: 'icons',
|
|
||||||
} satisfies LocalSpriteConfig
|
|
||||||
```
|
|
||||||
|
|
||||||
Локальный literal не разрешит случайно выбрать Vite или Next mode.
|
|
||||||
|
|||||||
@@ -1,151 +1,114 @@
|
|||||||
# Нативный icon Web Component с Vite
|
# SVG-спрайт для Vite без фреймворка
|
||||||
|
|
||||||
Это автономный quick start для exact mode key `standalone@vite`: generated facade регистрирует `<icons-icon>` и отдаёт SVG в asset pipeline Vite.
|
Инструкция по быстрому созданию SVG-спрайта в приложении на Vite без фреймворка.
|
||||||
|
|
||||||
## 1. Генерация спрайта
|
## Генерация спрайта
|
||||||
|
|
||||||
Главное преимущество: генератор не нужно устанавливать и добавлять в `package.json`. `npx` временно скачивает CLI, а generated production runtime не импортирует `@gromlab/svg-sprites`.
|
Выберите папку для спрайта. В примере используется `assets/app-icons`, а исходные SVG находятся в `assets/svg-icons`.
|
||||||
|
|
||||||
Рекомендуемая структура держит конфиг и иконки рядом:
|
Создайте конфиг `assets/app-icons/svg-sprite.config.json`:
|
||||||
|
|
||||||
```text
|
```json
|
||||||
src/sprite/
|
{
|
||||||
├── icons/
|
"mode": "standalone@vite",
|
||||||
│ ├── check.svg
|
"name": "app",
|
||||||
│ └── warning.svg
|
"input": "../svg-icons/**/*.svg"
|
||||||
├── index.ts
|
|
||||||
└── svg-sprite.config.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
Минимальный plain config:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default {
|
|
||||||
mode: 'standalone@vite',
|
|
||||||
name: 'icons',
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Если `input` не указан, SVG читаются из `./icons` относительно конфига. Конфиг также может быть `.js` с `default export` или `.json`.
|
Путь в `input` считается от папки с конфигом.
|
||||||
|
|
||||||
```bash
|
Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой:
|
||||||
npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
В CI замените `latest` на точную версию, например `@gromlab/svg-sprites@1.1.5`. Exact dev/build commands для Vite:
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts",
|
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
|
||||||
"dev": "npm run sprites && vite",
|
"predev": "npm run sprites",
|
||||||
"build": "npm run sprites && vite build",
|
"dev": "vite",
|
||||||
"typecheck": "npm run sprites && tsc --noEmit"
|
"prebuild": "npm run sprites",
|
||||||
|
"build": "vite build"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Не добавляйте одновременно `predev`/`prebuild` и явный `npm run sprites` в этих scripts. `.svg-sprite` является generated-каталогом и не коммитится. Generated локальный `.gitignore`, который исключает этот каталог, нужно добавить в Git один раз. Generated `.d.ts` self-contained и не импортируют `@gromlab/svg-sprites`.
|
## Использование спрайта
|
||||||
|
|
||||||
Верните facade через пользовательский barrel:
|
Значение `name: "app"` создаёт элемент `<app-icon>`.
|
||||||
|
|
||||||
|
Создайте точку входа `assets/app-icons/index.ts`:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
// src/sprite/index.ts
|
|
||||||
export * from './.svg-sprite/index.js'
|
export * from './.svg-sprite/index.js'
|
||||||
```
|
```
|
||||||
|
|
||||||
Production entry регистрирует native Web Component:
|
Зарегистрируйте элемент в `src/main.ts`:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { defineIconsIconElement, iconsIconNames } from './sprite'
|
import { defineAppIconElement } from '../assets/app-icons'
|
||||||
|
import './style.css'
|
||||||
|
|
||||||
defineIconsIconElement()
|
defineAppIconElement()
|
||||||
|
|
||||||
document.querySelector<HTMLDivElement>('#app')!.innerHTML = `
|
|
||||||
<icons-icon icon="check" role="img" aria-label="Готово"></icons-icon>
|
|
||||||
`
|
|
||||||
|
|
||||||
console.log(iconsIconNames)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Generated facade импортирует `sprite.svg?no-inline`: Vite автоматически выпускает отдельный hashed SVG asset и не превращает его в data URL. TypeScript-проекту при необходимости добавьте стандартные Vite types:
|
Используйте иконку в HTML:
|
||||||
|
|
||||||
```ts
|
```html
|
||||||
/// <reference types="vite/client" />
|
<app-icon icon="check" role="img" aria-label="Готово"></app-icon>
|
||||||
```
|
```
|
||||||
|
|
||||||
Размер по умолчанию равен `1em`, поэтому компонент удобно масштабировать через `font-size`. Цвет задаётся через `color` и generated custom properties:
|
Файл `check.svg` доступен как `icon="check"`. Размер и цвета настраиваются через CSS:
|
||||||
|
|
||||||
```css
|
```css
|
||||||
icons-icon {
|
app-icon {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
color: #334155;
|
color: #334155;
|
||||||
--icon-color-2: #f59e0b;
|
--icon-color-2: #f59e0b;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## 2. Дебаг и превью
|
Монохромная иконка наследует `color`, а цвета многоцветной иконки переопределяются через `--icon-color-N`. Нужные переменные показывает Viewer.
|
||||||
|
|
||||||
Viewer необязателен и нужен только для debug/preview. Установите его отдельно:
|
Vite сам добавит `sprite.svg` в итоговую сборку. Копировать его в `public` не нужно.
|
||||||
|
|
||||||
|
## Дебаг и превью
|
||||||
|
|
||||||
|
Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки и устанавливается отдельно:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install --save-dev @gromlab/svg-sprites
|
npm install --save-dev @gromlab/svg-sprites
|
||||||
```
|
```
|
||||||
|
|
||||||
Зарегистрируйте Viewer и передайте generated JS manifest через свойство `sources`:
|
Создайте `svg-sprite.html` в корне проекта:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Иконки проекта</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- Компонент Viewer для дебага и превью SVG-спрайта -->
|
||||||
|
<gromlab-sprite-viewer viewer-title="Иконки проекта"></gromlab-sprite-viewer>
|
||||||
|
|
||||||
|
<!-- Подключение создаваемого ниже скрипта дебаггера -->
|
||||||
|
<script type="module" src="/src/svg-sprite-debug.ts"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
Создайте `src/svg-sprite-debug.ts`:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import '@gromlab/svg-sprites/viewer/element'
|
import '@gromlab/svg-sprites/viewer/element'
|
||||||
import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer'
|
import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer'
|
||||||
import spriteManifest from './sprite/.svg-sprite/svg-sprite.manifest.js'
|
import spriteManifest from '../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'
|
||||||
|
|
||||||
document.querySelector<HTMLDivElement>('#app')!.insertAdjacentHTML(
|
|
||||||
'beforeend',
|
|
||||||
'<gromlab-sprite-viewer></gromlab-sprite-viewer>',
|
|
||||||
)
|
|
||||||
|
|
||||||
const viewer = document.querySelector<SpriteViewerElement>('gromlab-sprite-viewer')!
|
const viewer = document.querySelector<SpriteViewerElement>('gromlab-sprite-viewer')!
|
||||||
viewer.viewerTitle = 'Иконки проекта'
|
|
||||||
viewer.sources = [spriteManifest]
|
viewer.sources = [spriteManifest]
|
||||||
```
|
```
|
||||||
|
|
||||||
Расположите этот код только в debug entry или внутреннем маршруте. Viewer не входит в production runtime `<icons-icon>`.
|
Запустите `npm run dev` и откройте `/svg-sprite.html`.
|
||||||
|
|
||||||
## 3. Типизация конфига
|
Viewer не требуется для работы `<app-icon>` и не подключается к основному коду приложения.
|
||||||
|
|
||||||
При локально установленном package используйте helper:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineSpriteConfig } from '@gromlab/svg-sprites'
|
|
||||||
|
|
||||||
export default defineSpriteConfig({
|
|
||||||
mode: 'standalone@vite',
|
|
||||||
name: 'icons',
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Альтернатива с package: `import type { SpriteConfig }` и объект `satisfies SpriteConfig`.
|
|
||||||
|
|
||||||
Без package скопируйте локальный type прямо в config:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
type LocalSpriteConfig = {
|
|
||||||
mode: 'standalone@vite'
|
|
||||||
name?: string
|
|
||||||
description?: string
|
|
||||||
input?: string | string[]
|
|
||||||
transform?: {
|
|
||||||
removeSize?: boolean
|
|
||||||
replaceColors?: boolean
|
|
||||||
addTransition?: boolean
|
|
||||||
}
|
|
||||||
generatedNotice?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
mode: 'standalone@vite',
|
|
||||||
name: 'icons',
|
|
||||||
} satisfies LocalSpriteConfig
|
|
||||||
```
|
|
||||||
|
|
||||||
Локальный type ограничивает `mode` одним exact literal и ничего не загружает во время генерации.
|
|
||||||
|
|||||||
@@ -1,145 +1,111 @@
|
|||||||
# Нативный icon Web Component с Webpack 5
|
# SVG-спрайт для Webpack 5 без фреймворка
|
||||||
|
|
||||||
Это автономный quick start для exact mode key `standalone@webpack`: generated facade предоставляет `<icons-icon>`, а Webpack 5 публикует SVG через Asset Modules.
|
Инструкция по быстрому созданию SVG-спрайта в приложении на Webpack 5 без фреймворка.
|
||||||
|
|
||||||
## 1. Генерация спрайта
|
## Генерация спрайта
|
||||||
|
|
||||||
Главное преимущество: генератор не нужно устанавливать и добавлять в `package.json`. `npx` временно скачивает CLI, а generated production runtime не импортирует `@gromlab/svg-sprites`.
|
Выберите папку для спрайта. В примере используется `assets/app-icons`, а исходные SVG находятся в `assets/svg-icons`.
|
||||||
|
|
||||||
```text
|
Создайте конфиг `assets/app-icons/svg-sprite.config.json`:
|
||||||
src/sprite/
|
|
||||||
├── icons/
|
|
||||||
│ ├── check.svg
|
|
||||||
│ └── warning.svg
|
|
||||||
├── index.ts
|
|
||||||
└── svg-sprite.config.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
Минимальный config рядом с `icons/`:
|
```json
|
||||||
|
{
|
||||||
```ts
|
"mode": "standalone@webpack",
|
||||||
export default {
|
"name": "app",
|
||||||
mode: 'standalone@webpack',
|
"input": "../svg-icons/**/*.svg"
|
||||||
name: 'icons',
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Если `input` не указан, SVG читаются из `./icons` относительно конфига. Поддерживаются также `.js` с `default export` и `.json`.
|
Путь в `input` считается от папки с конфигом.
|
||||||
|
|
||||||
```bash
|
Добавьте команды генерации в `package.json`. Сгенерированные файлы по умолчанию исключены из Git, поэтому `predev` и `prebuild` пересобирают спрайт перед каждым запуском и сборкой:
|
||||||
npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
Для CI закрепите точную версию, например `@gromlab/svg-sprites@1.1.5`. Exact Webpack commands:
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/sprite/svg-sprite.config.ts",
|
"sprites": "npx --yes @gromlab/svg-sprites assets/app-icons/svg-sprite.config.json",
|
||||||
"dev": "npm run sprites && webpack serve --mode development",
|
"predev": "npm run sprites",
|
||||||
"build": "npm run sprites && webpack --mode production",
|
"dev": "webpack serve --mode development",
|
||||||
"typecheck": "npm run sprites && tsc --noEmit"
|
"prebuild": "npm run sprites",
|
||||||
|
"build": "webpack --mode production"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Не дублируйте запуск через `predev`/`prebuild`, если scripts уже явно вызывают `npm run sprites`. Generated `.svg-sprite` не коммитится. Generated локальный `.gitignore`, который исключает этот каталог, нужно добавить в Git один раз. Его declarations self-contained и не требуют `@gromlab/svg-sprites`.
|
## Использование спрайта
|
||||||
|
|
||||||
|
Значение `name: "app"` создаёт элемент `<app-icon>`.
|
||||||
|
|
||||||
|
Создайте точку входа `assets/app-icons/index.ts`:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
// src/sprite/index.ts
|
|
||||||
export * from './.svg-sprite/index.js'
|
export * from './.svg-sprite/index.js'
|
||||||
```
|
```
|
||||||
|
|
||||||
Production usage:
|
Зарегистрируйте элемент в основном entry приложения:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { defineIconsIconElement, iconsIconNames } from './sprite'
|
import { defineAppIconElement } from '../assets/app-icons'
|
||||||
|
import './style.css'
|
||||||
|
|
||||||
defineIconsIconElement()
|
defineAppIconElement()
|
||||||
|
|
||||||
document.querySelector<HTMLDivElement>('#app')!.innerHTML = `
|
|
||||||
<icons-icon icon="check" role="img" aria-label="Готово"></icons-icon>
|
|
||||||
`
|
|
||||||
|
|
||||||
console.log(iconsIconNames)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Generated facade использует `new URL('./sprite.svg', import.meta.url).href`. Webpack 5 Asset Modules выпускают отдельный asset; его итоговый URL учитывает `output.publicPath` и `assetModuleFilename`.
|
Используйте иконку в HTML:
|
||||||
|
|
||||||
Если проект использует `@svgr/webpack`, `svg-inline-loader`, `raw-loader` или общий SVG rule, исключите `src/sprite/.svg-sprite/sprite.svg` из этого правила. Generated SVG должен обрабатываться как `asset/resource`, а не как React-компонент или inline source.
|
```html
|
||||||
|
<app-icon icon="check" role="img" aria-label="Готово"></app-icon>
|
||||||
|
```
|
||||||
|
|
||||||
Размер Web Component по умолчанию `1em`; управляйте им и цветами обычным CSS:
|
Файл `check.svg` доступен как `icon="check"`. Размер и цвета настраиваются через CSS:
|
||||||
|
|
||||||
```css
|
```css
|
||||||
icons-icon {
|
app-icon {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
color: #334155;
|
color: #334155;
|
||||||
--icon-color-2: #f59e0b;
|
--icon-color-2: #f59e0b;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## 2. Дебаг и превью
|
Монохромная иконка наследует `color`, а цвета многоцветной иконки переопределяются через `--icon-color-N`. Нужные переменные показывает Viewer.
|
||||||
|
|
||||||
Viewer необязателен. Для debug/preview установите package как dev dependency:
|
Webpack 5 сам добавит `sprite.svg` в итоговую сборку.
|
||||||
|
|
||||||
|
## Дебаг и превью
|
||||||
|
|
||||||
|
Viewer показывает все иконки на одной странице, позволяет проверить их отображение, изменить цвета и посмотреть связанные CSS-переменные. Он нужен только для разработки.
|
||||||
|
|
||||||
|
Установите Viewer:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install --save-dev @gromlab/svg-sprites
|
npm install --save-dev @gromlab/svg-sprites
|
||||||
```
|
```
|
||||||
|
|
||||||
Подключите element entry и generated JS manifest:
|
Создайте entry `src/svg-sprite-debug.ts`:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import '@gromlab/svg-sprites/viewer/element'
|
import '@gromlab/svg-sprites/viewer/element'
|
||||||
import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer'
|
import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer'
|
||||||
import spriteManifest from './sprite/.svg-sprite/svg-sprite.manifest.js'
|
import spriteManifest from '../assets/app-icons/.svg-sprite/svg-sprite.manifest.js'
|
||||||
|
|
||||||
document.querySelector<HTMLDivElement>('#app')!.insertAdjacentHTML(
|
const viewer = document.createElement('gromlab-sprite-viewer') as SpriteViewerElement
|
||||||
'beforeend',
|
|
||||||
'<gromlab-sprite-viewer></gromlab-sprite-viewer>',
|
|
||||||
)
|
|
||||||
|
|
||||||
const viewer = document.querySelector<SpriteViewerElement>('gromlab-sprite-viewer')!
|
|
||||||
viewer.viewerTitle = 'Иконки проекта'
|
viewer.viewerTitle = 'Иконки проекта'
|
||||||
viewer.sources = [spriteManifest]
|
viewer.sources = [spriteManifest]
|
||||||
|
document.body.append(viewer)
|
||||||
```
|
```
|
||||||
|
|
||||||
Webpack свяжет manifest с тем же emitted SVG asset. Оставляйте Viewer только в debug entry: production `<icons-icon>` от него не зависит.
|
Добавьте скрипт к основному entry только в development-режиме. Сохраните остальные настройки `webpack.config.js`:
|
||||||
|
|
||||||
## 3. Типизация конфига
|
```js
|
||||||
|
export default (_env, argv) => ({
|
||||||
После локальной установки package можно использовать helper:
|
// Остальные настройки Webpack.
|
||||||
|
entry: [
|
||||||
```ts
|
'./src/main.ts',
|
||||||
import { defineSpriteConfig } from '@gromlab/svg-sprites'
|
...(argv.mode === 'development' ? ['./src/svg-sprite-debug.ts'] : []),
|
||||||
|
],
|
||||||
export default defineSpriteConfig({
|
|
||||||
mode: 'standalone@webpack',
|
|
||||||
name: 'icons',
|
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
Либо импортируйте только `SpriteConfig` как type и примените `satisfies SpriteConfig`.
|
Запустите `npm run dev`. Viewer появится на основной странице приложения.
|
||||||
|
|
||||||
Без package добавьте copy-paste type в сам config:
|
Viewer добавляется только в development-сборку и не попадает в production.
|
||||||
|
|
||||||
```ts
|
|
||||||
type LocalSpriteConfig = {
|
|
||||||
mode: 'standalone@webpack'
|
|
||||||
name?: string
|
|
||||||
description?: string
|
|
||||||
input?: string | string[]
|
|
||||||
transform?: {
|
|
||||||
removeSize?: boolean
|
|
||||||
replaceColors?: boolean
|
|
||||||
addTransition?: boolean
|
|
||||||
}
|
|
||||||
generatedNotice?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
mode: 'standalone@webpack',
|
|
||||||
name: 'icons',
|
|
||||||
} satisfies LocalSpriteConfig
|
|
||||||
```
|
|
||||||
|
|
||||||
Этот вариант сохраняет проверку exact mode без runtime import и без записи generator package в проект.
|
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
# Guides Next.js App Router перемещены
|
|
||||||
|
|
||||||
- [App Router + Turbopack](guides/next-app-turbopack.md)
|
|
||||||
- [App Router + Webpack](guides/next-app-webpack.md)
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
# Guides Next.js Pages Router перемещены
|
|
||||||
|
|
||||||
- [Pages Router + Turbopack](guides/next-pages-turbopack.md)
|
|
||||||
- [Pages Router + Webpack](guides/next-pages-webpack.md)
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Программный API перемещён
|
|
||||||
|
|
||||||
Canonical документ: [программный API](reference/programmatic-api.md).
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Guide React + Vite перемещён
|
|
||||||
|
|
||||||
Canonical guide: [React + Vite](guides/react-vite.md).
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Guide React + Webpack перемещён
|
|
||||||
|
|
||||||
Canonical guide: [React + Webpack](guides/react-webpack.md).
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Технический справочник перемещён
|
|
||||||
|
|
||||||
Canonical документ: [технический справочник](reference/technical.md).
|
|
||||||
@@ -14,6 +14,21 @@ const result = await generateSprite(
|
|||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Результат содержит имя и mode спрайта, количество иконок и абсолютные filesystem paths:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
result.name
|
||||||
|
result.mode
|
||||||
|
result.target
|
||||||
|
result.iconCount
|
||||||
|
result.rootDir
|
||||||
|
result.generatedDir
|
||||||
|
result.spritePath
|
||||||
|
result.manifestPath
|
||||||
|
```
|
||||||
|
|
||||||
|
Next.js modes дополнительно возвращают `router` и `bundler`.
|
||||||
|
|
||||||
Для static standalone mode `result.spritePath` можно использовать в build-скрипте,
|
Для static standalone mode `result.spritePath` можно использовать в build-скрипте,
|
||||||
чтобы опубликовать SVG по URL приложения:
|
чтобы опубликовать SVG по URL приложения:
|
||||||
|
|
||||||
@@ -29,7 +44,7 @@ await copyFile(result.spritePath, 'dist/app-icons/sprite.svg')
|
|||||||
`spritePath` является filesystem path, а не browser URL. Deployment-neutral JSON
|
`spritePath` является filesystem path, а не browser URL. Deployment-neutral JSON
|
||||||
manifest доступен через `result.manifestPath` и копируется независимо от SVG.
|
manifest доступен через `result.manifestPath` и копируется независимо от SVG.
|
||||||
|
|
||||||
Первый аргумент принимает полный путь к config-файлу с любым именем и расширением `.ts`, `.js` или `.json`. Каталог вместо файла включает config-less режим: корнем sprite-модуля становится этот каталог.
|
Первый аргумент принимает абсолютный или относительный путь к config-файлу с любым именем и расширением `.ts`, `.js` или `.json`. Каталог вместо файла включает config-less режим: корнем sprite-модуля становится этот каталог.
|
||||||
|
|
||||||
Второй аргумент содержит необязательные overrides и всегда имеет приоритет над конфигом:
|
Второй аргумент содержит необязательные overrides и всегда имеет приоритет над конфигом:
|
||||||
|
|
||||||
@@ -107,13 +122,17 @@ await generateNextSprite('path/to/config.ts', {
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
import {
|
import {
|
||||||
|
isSpriteMode,
|
||||||
loadSpriteConfig,
|
loadSpriteConfig,
|
||||||
resolveSpriteConfig,
|
resolveSpriteConfig,
|
||||||
|
resolveSpriteConfigSource,
|
||||||
validateSpriteConfig,
|
validateSpriteConfig,
|
||||||
} from '@gromlab/svg-sprites'
|
} from '@gromlab/svg-sprites'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
- `isSpriteMode(value)` проверяет, является ли значение поддерживаемым exact mode.
|
||||||
- `loadSpriteConfig(file)` загружает явно указанный `.ts`, `.js` или `.json` файл.
|
- `loadSpriteConfig(file)` загружает явно указанный `.ts`, `.js` или `.json` файл.
|
||||||
|
- `resolveSpriteConfigSource(source)` разрешает путь как config-файл или config-less каталог.
|
||||||
- `validateSpriteConfig(value)` выполняет runtime-валидацию объекта.
|
- `validateSpriteConfig(value)` выполняет runtime-валидацию объекта.
|
||||||
- `resolveSpriteConfig(root, config, overrides)` объединяет значения, добавляет defaults и разрешает пути относительно `root`.
|
- `resolveSpriteConfig(root, config, overrides)` объединяет значения, добавляет defaults и разрешает пути относительно `root`.
|
||||||
|
|
||||||
@@ -138,6 +157,16 @@ import type { SpriteViewerElement } from '@gromlab/svg-sprites/viewer'
|
|||||||
|
|
||||||
Browser entry регистрирует `<gromlab-sprite-viewer>`. Bare standalone также может загрузить самостоятельный `dist/viewer-element.js` без bundler.
|
Browser entry регистрирует `<gromlab-sprite-viewer>`. Bare standalone также может загрузить самостоятельный `dist/viewer-element.js` без bundler.
|
||||||
|
|
||||||
|
Для ручной регистрации импортируйте runtime без auto-register entry:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { defineSpriteViewerElement } from '@gromlab/svg-sprites/viewer'
|
||||||
|
|
||||||
|
defineSpriteViewerElement()
|
||||||
|
```
|
||||||
|
|
||||||
|
Этот entry также экспортирует типы `SpriteViewerElement`, `SpriteViewerManifest`, `SpriteViewerSource`, `SpriteViewerSources` и связанные типы manifest и loaders.
|
||||||
|
|
||||||
React bridge сохраняет компонентный API:
|
React bridge сохраняет компонентный API:
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
[Индекс документации](../README.md)
|
[Индекс документации](../README.md)
|
||||||
|
|
||||||
|
[Конфигурация JSON, JavaScript и TypeScript](../configuration.md)
|
||||||
|
|
||||||
Справочник по конфигурации, generated API и поведению `@gromlab/svg-sprites`. Пошаговую установку смотрите в руководстве для вашего стека:
|
Справочник по конфигурации, generated API и поведению `@gromlab/svg-sprites`. Пошаговую установку смотрите в руководстве для вашего стека:
|
||||||
|
|
||||||
- [Bare standalone](../guides/standalone.md)
|
- [Bare standalone](../guides/standalone.md)
|
||||||
@@ -24,7 +26,7 @@
|
|||||||
Для генерации не нужна dependency проекта. Запускайте CLI через `npx`:
|
Для генерации не нужна dependency проекта. Запускайте CLI через `npx`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npx --yes --package=@gromlab/svg-sprites@latest svg-sprites path/to/svg-sprite.config.ts
|
npx --yes @gromlab/svg-sprites path/to/svg-sprite.config.json
|
||||||
```
|
```
|
||||||
|
|
||||||
Устанавливайте пакет как development dependency, только если проекту нужны
|
Устанавливайте пакет как development dependency, только если проекту нужны
|
||||||
@@ -36,7 +38,7 @@ npm install --save-dev @gromlab/svg-sprites
|
|||||||
|
|
||||||
## CLI и режимы генерации
|
## CLI и режимы генерации
|
||||||
|
|
||||||
CLI принимает ровно один путь: явно выбранный config-файл либо каталог для config-less генерации:
|
Для генерации CLI принимает ровно один путь: явно выбранный config-файл либо каталог для config-less генерации:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
svg-sprites [options] <config-file-or-directory>
|
svg-sprites [options] <config-file-or-directory>
|
||||||
@@ -54,11 +56,11 @@ svg-sprites [options] <config-file-or-directory>
|
|||||||
| Next.js Pages Router + Turbopack | `next@pages/turbopack` |
|
| Next.js Pages Router + Turbopack | `next@pages/turbopack` |
|
||||||
| Next.js Pages Router + Webpack 5 | `next@pages/webpack` |
|
| Next.js Pages Router + Webpack 5 | `next@pages/webpack` |
|
||||||
|
|
||||||
Config-файл может иметь любое имя и расширение `.ts`, `.js` или `.json`. CLI не ищет конфиг по соглашению: файл нужно передать явно. В руководствах используется рекомендуемое имя `svg-sprite.config.ts`.
|
Config-файл может иметь любое имя и расширение `.ts`, `.js` или `.json`. CLI не ищет конфиг по соглашению: файл нужно передать явно. В руководствах используется рекомендуемое имя `svg-sprite.config.json`.
|
||||||
|
|
||||||
Если передан каталог, все настройки берутся из CLI. Если передан config-файл, CLI-параметры перекрывают значения файла. Общий порядок: `defaults → config → CLI`.
|
Если передан каталог, все настройки берутся из CLI. Если передан config-файл, CLI-параметры перекрывают значения файла. Общий порядок: `defaults → config → CLI`.
|
||||||
|
|
||||||
Доступны `--mode`, `--name`, `--description`, повторяемый `--input <path-or-glob>`, а также пары `--remove-size`/`--no-remove-size`, `--replace-colors`/`--no-replace-colors`, `--add-transition`/`--no-add-transition` и `--generated-notice`/`--no-generated-notice`. Переданные transform-флаги перекрывают отдельные поля, а хотя бы один `--input` полностью заменяет значение `input` из config.
|
`--help` и `-h` выводят справку без обязательного пути. Для генерации доступны `--mode`, `--name`, `--description`, повторяемый `--input <path-or-glob>`, а также пары `--remove-size`/`--no-remove-size`, `--replace-colors`/`--no-replace-colors`, `--add-transition`/`--no-add-transition` и `--generated-notice`/`--no-generated-notice`. Переданные transform-флаги перекрывают отдельные поля, а хотя бы один `--input` полностью заменяет значение `input` из config.
|
||||||
|
|
||||||
В CLI заключайте glob-паттерны в одинарные кавычки, чтобы shell не раскрыл их до запуска генератора:
|
В CLI заключайте glob-паттерны в одинарные кавычки, чтобы shell не раскрыл их до запуска генератора:
|
||||||
|
|
||||||
@@ -96,7 +98,7 @@ export default defineSpriteConfig({
|
|||||||
| Опция | Тип | По умолчанию | Назначение |
|
| Опция | Тип | По умолчанию | Назначение |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `mode` | `SpriteMode` | Нет | Режим генерации; можно передать через CLI/API |
|
| `mode` | `SpriteMode` | Нет | Режим генерации; можно передать через CLI/API |
|
||||||
| `name` | `string` | Выводится из каталога | Имя спрайта, компонента и публичных типов |
|
| `name` | `string` | Выводится из каталога | Имя спрайта; в modes с компонентом также задаёт имя компонента и публичных типов |
|
||||||
| `description` | `string` | Нет | Описание для типов и debug manifest |
|
| `description` | `string` | Нет | Описание для типов и debug manifest |
|
||||||
| `input` | `string \| string[]` | `./icons` | Папки, SVG-файлы и glob-паттерны относительно папки конфига |
|
| `input` | `string \| string[]` | `./icons` | Папки, SVG-файлы и glob-паттерны относительно папки конфига |
|
||||||
| `transform` | `TransformOptions` | Все включены | Настройки подготовки SVG |
|
| `transform` | `TransformOptions` | Все включены | Настройки подготовки SVG |
|
||||||
@@ -111,7 +113,7 @@ app → AppIcon
|
|||||||
file-manager → FileManagerIcon
|
file-manager → FileManagerIcon
|
||||||
```
|
```
|
||||||
|
|
||||||
Если `name` не задано, генератор выводит его из каталога. Для каталога с именем `svg-sprite` или `svg-sprites` используется имя родительского каталога.
|
Если `name` не задано, генератор преобразует имя каталога в kebab-case. Для каталога с именем `svg-sprite` или `svg-sprites` используется имя родительского каталога.
|
||||||
|
|
||||||
### Источники иконок
|
### Источники иконок
|
||||||
|
|
||||||
@@ -141,7 +143,7 @@ file-manager → FileManagerIcon
|
|||||||
```text
|
```text
|
||||||
app-icons/
|
app-icons/
|
||||||
├── .gitignore
|
├── .gitignore
|
||||||
├── svg-sprite.config.ts
|
├── svg-sprite.config.json
|
||||||
├── index.ts # необязательный пользовательский barrel
|
├── index.ts # необязательный пользовательский barrel
|
||||||
└── .svg-sprite/
|
└── .svg-sprite/
|
||||||
├── index.js
|
├── index.js
|
||||||
@@ -183,10 +185,10 @@ runtime asset и deployment-neutral manifest data:
|
|||||||
generated Web Component без внешних runtime-зависимостей. Bare `standalone`
|
generated Web Component без внешних runtime-зависимостей. Bare `standalone`
|
||||||
намеренно не создаёт JavaScript-компонент.
|
намеренно не создаёт JavaScript-компонент.
|
||||||
|
|
||||||
Генератор перезаписывает и удаляет только файлы со своим marker. Если в managed-пути находится пользовательский файл, генерация завершается ошибкой. Корневой `index.ts` генератору не принадлежит; при необходимости создайте пользовательский barrel:
|
`.svg-sprite` полностью управляется генератором и при каждой генерации заменяется целиком. Любые добавленные в него файлы будут удалены. Пользовательские файлы размещайте рядом, например в корневом `index.ts`:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
export * from './.svg-sprite'
|
export * from './.svg-sprite/index.js'
|
||||||
```
|
```
|
||||||
|
|
||||||
## Standalone Web Component и TypeScript
|
## Standalone Web Component и TypeScript
|
||||||
@@ -307,7 +309,7 @@ folder open.svg → icon="folder open" → id="icon-<stable-hash>"
|
|||||||
|
|
||||||
## Множественные спрайты
|
## Множественные спрайты
|
||||||
|
|
||||||
Каждый каталог с конфигом создаёт независимый компонент, типы, manifest и SVG asset:
|
Каждый каталог с конфигом создаёт независимый mode-specific контракт. React и Next.js создают React-компонент и типы, `standalone@vite` и `standalone@webpack` — Web Component и типы, а bare `standalone` — SVG и JSON manifest:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
app-icons → AppIcon → общие иконки
|
app-icons → AppIcon → общие иконки
|
||||||
@@ -353,7 +355,7 @@ Static HTML после публикации `.svg-sprite/sprite.svg` прило
|
|||||||
</svg>
|
</svg>
|
||||||
```
|
```
|
||||||
|
|
||||||
Standalone Vite/Webpack предоставляет generated `getIconsIconHref()` и mapping
|
Standalone Vite/Webpack предоставляет generated `getAppIconHref()` и mapping
|
||||||
внутренних IDs. Не конструируйте fragment из небезопасного имени файла вручную.
|
внутренних IDs. Не конструируйте fragment из небезопасного имени файла вручную.
|
||||||
|
|
||||||
Vite:
|
Vite:
|
||||||
@@ -602,7 +604,7 @@ Viewer показывает группы, поиск, `viewBox`, CSS-перем
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"sprites": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/app-icons/svg-sprite.config.ts",
|
"sprites": "npx --yes @gromlab/svg-sprites src/ui/app-icons/svg-sprite.config.ts",
|
||||||
"predev": "npm run sprites",
|
"predev": "npm run sprites",
|
||||||
"prebuild": "npm run sprites",
|
"prebuild": "npm run sprites",
|
||||||
"pretypecheck": "npm run sprites"
|
"pretypecheck": "npm run sprites"
|
||||||
@@ -610,22 +612,22 @@ Viewer показывает группы, поиск, `viewBox`, CSS-перем
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
CI должен выполнять generation script до сборки или проверки типов. Для воспроизводимости замените `latest` на точную версию. Локальная установка package не нужна, если CI не использует Viewer, package-типы config или программный API.
|
CI должен выполнять generation script до сборки или проверки типов. Локальная установка package не нужна, если CI не использует Viewer, package-типы config или программный API.
|
||||||
|
|
||||||
Bare `standalone` не создаёт и не изменяет `.gitignore`: приложение само решает, коммитить или игнорировать его `.svg-sprite/`. В остальных modes генератор не перезапишет пользовательский `.gitignore`. Он также откажется перезаписывать пользовательский файл внутри `.svg-sprite`. Корневой `index.ts` остаётся пользовательским и может переэкспортировать generated API.
|
Bare `standalone` не создаёт `.gitignore` и сохраняет пользовательский файл. Если после другого mode остался управляемый `.gitignore`, bare mode удалит его. В остальных modes генератор откажется перезаписать пользовательский `.gitignore` без generated marker. Корневой `index.ts` остаётся пользовательским и может переэкспортировать generated API.
|
||||||
|
|
||||||
## Диагностика
|
## Диагностика
|
||||||
|
|
||||||
- Нет `.svg-sprite/index.js`: запустите generation script до импорта generated-модуля.
|
- Для всех modes, кроме bare `standalone`: если нет `.svg-sprite/index.js`, запустите generation script до импорта generated-модуля.
|
||||||
- Не найден источник: передайте существующий config-файл или каталог sprite-модуля.
|
- Не найден источник: передайте существующий config-файл или каталог sprite-модуля.
|
||||||
- Не указан mode: добавьте `mode` в config либо передайте `--mode`.
|
- Не указан mode: добавьте `mode` в config либо передайте `--mode`.
|
||||||
- Иконка отсутствует в типе: проверьте `input`, расширение `.svg`, glob-исключения и необходимость `**/*.svg` для вложенных папок.
|
- Иконка отсутствует в типе: проверьте `input`, расширение `.svg`, glob-исключения и необходимость `**/*.svg` для вложенных папок.
|
||||||
- Конфликт имени: два разных SVG имеют одинаковый basename; переименуйте один файл.
|
- Конфликт имени: два разных SVG имеют одинаковый basename; переименуйте один файл.
|
||||||
- `Refusing to overwrite a user file`: в managed-пути находится файл без generated marker.
|
- `Refusing to overwrite a user file`: в корне sprite-модуля находится пользовательский `.gitignore`, который генератор не может заменить.
|
||||||
- Иконка не меняет цвет: используйте `<svg><use>` или generated-компонент и проверьте `replaceColors`.
|
- Иконка не меняет цвет: используйте `<svg><use>` или generated-компонент и проверьте `replaceColors`.
|
||||||
- Webpack выдаёт неверный URL: проверьте Asset Modules, `output.publicPath` и SVG loaders.
|
- Webpack выдаёт неверный URL: проверьте Asset Modules, `output.publicPath` и SVG loaders.
|
||||||
- Static sprite возвращает 404: проверьте post-generation copy или server alias и не передавайте filesystem `spritePath` в HTML.
|
- Static sprite возвращает 404: проверьте post-generation copy или server alias и не передавайте filesystem `spritePath` в HTML.
|
||||||
- Viewer не видит спрайт: проверьте путь к `.svg-sprite/svg-sprite.manifest.js` и выполните генерацию до запуска приложения.
|
- Viewer не видит спрайт: для bundler modes проверьте путь к `.svg-sprite/svg-sprite.manifest.js`; для bare `standalone` — URL опубликованных `svg-sprite.manifest.json` и `sprite.svg`. Выполните генерацию до запуска приложения.
|
||||||
- Build и mode не совпадают: используйте target, соответствующий фактическому сборщику.
|
- Build и mode не совпадают: используйте target, соответствующий фактическому сборщику.
|
||||||
|
|
||||||
Для собственного orchestration и низкоуровневой компиляции смотрите [Программный API](programmatic-api.md).
|
Для собственного orchestration и низкоуровневой компиляции смотрите [Программный API](programmatic-api.md).
|
||||||
|
|||||||
@@ -1,31 +1,25 @@
|
|||||||
# AI skills
|
# AI skills
|
||||||
|
|
||||||
Исходники обязательного контекста английского и русского skills находятся в `skills/svg-sprites/src/{en,ru}/`. Канонические exact-mode guides находятся в `docs/{en,ru}/guides/` и копируются в соответствующий skill без изменения. Готовые переносимые артефакты генерируются в `skills/artifacts/`, игнорируются Git и упаковываются в ZIP во время release workflow.
|
Исходники обязательного контекста английского и русского skills находятся в `skills/svg-sprites/src/{en,ru}/`. Готовые переносимые артефакты генерируются в `skills/artifacts/`, игнорируются Git и упаковываются в ZIP во время release workflow.
|
||||||
|
|
||||||
Обе языковые версии имеют одинаковую структуру:
|
Русский skill хранит весь обязательный контекст в одном файле:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
src/<language>/
|
src/ru/
|
||||||
├── SKILL.md
|
├── SKILL.md
|
||||||
├── core/
|
|
||||||
│ ├── 00-package-overview.md
|
|
||||||
│ ├── 10-mode-selection.md
|
|
||||||
│ ├── 20-project-inspection.md
|
|
||||||
│ ├── 30-react-next-setup.md
|
|
||||||
│ ├── 40-generated-contract.md
|
|
||||||
│ ├── 50-usage-and-colors.md
|
|
||||||
│ ├── 60-verification.md
|
|
||||||
│ └── 70-diagnostics.md
|
|
||||||
└── references/
|
└── references/
|
||||||
├── programmatic-api.md
|
|
||||||
└── complex-svg.md
|
└── complex-svg.md
|
||||||
```
|
```
|
||||||
|
|
||||||
`core/` содержит обязательные знания, раскрываемые прямо в итоговый `SKILL.md`. Локальный `references/` содержит только agent-specific материалы. Девять файлов из `docs/<language>/guides/` копируются в `references/guides/`; английский artifact получает только английские guides, русский только русские. Второй набор mode guides и каталог `references/upstream/` не создаются.
|
`src/ru/SKILL.md` содержит знания о пакете и рабочий процесс агента. Exact-mode настройка берётся из canonical guides, а не дублируется отдельными source-фрагментами.
|
||||||
|
|
||||||
|
Русский artifact дополнительно получает без изменений `README_RU.md` и содержательную пользовательскую документацию из `docs/ru/`. Локальный редакторский `guides/AGENTS.md`, а также навигационные `guides/README.md` и `reference/README.md` не копируются. Файлы находятся в `references/README_RU.md` и `references/docs/ru/`. Agent-specific `complex-svg.md` остаётся отдельным reference.
|
||||||
|
|
||||||
|
Английский source пока сохраняет составную структуру с `core/`, а artifact — английские exact-mode guides и локальные `programmatic-api.md`/`complex-svg.md`. Его перевод на единый `SKILL.md` и полную canonical-документацию выполняется отдельно.
|
||||||
|
|
||||||
## Композиция Markdown
|
## Композиция Markdown
|
||||||
|
|
||||||
В любой собираемый документ можно включать фрагменты:
|
Сборщик сохраняет поддержку Markdown includes для английского skill и будущих документов:
|
||||||
|
|
||||||
```md
|
```md
|
||||||
<!-- include: ./core/10-mode-selection.md -->
|
<!-- include: ./core/10-mode-selection.md -->
|
||||||
|
|||||||
@@ -137,7 +137,13 @@ function expandCopies(config) {
|
|||||||
assertSafeRelativePath(entry.toDirectory)
|
assertSafeRelativePath(entry.toDirectory)
|
||||||
const sourceDirectory = path.resolve(skillDir, entry.fromDirectory)
|
const sourceDirectory = path.resolve(skillDir, entry.fromDirectory)
|
||||||
const extensions = entry.extensions ?? []
|
const extensions = entry.extensions ?? []
|
||||||
for (const relativePath of listDirectoryFiles(sourceDirectory, extensions)) {
|
const sourceFiles = listDirectoryFiles(sourceDirectory, extensions)
|
||||||
|
const excluded = new Set((entry.exclude ?? []).map((relativePath) => {
|
||||||
|
assertSafeRelativePath(relativePath)
|
||||||
|
return relativePath.replaceAll('\\', '/')
|
||||||
|
}))
|
||||||
|
for (const relativePath of sourceFiles) {
|
||||||
|
if (excluded.has(relativePath)) continue
|
||||||
copies.push({
|
copies.push({
|
||||||
from: path.join(sourceDirectory, relativePath),
|
from: path.join(sourceDirectory, relativePath),
|
||||||
to: path.posix.join(entry.toDirectory, relativePath),
|
to: path.posix.join(entry.toDirectory, relativePath),
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
const agentReferences = [
|
const agentReferences = {
|
||||||
'programmatic-api.md',
|
en: [
|
||||||
'complex-svg.md',
|
'programmatic-api.md',
|
||||||
]
|
'complex-svg.md',
|
||||||
|
],
|
||||||
|
ru: [
|
||||||
|
'complex-svg.md',
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
const guideFiles = [
|
const guideFiles = [
|
||||||
'standalone.md',
|
'standalone.md',
|
||||||
@@ -18,7 +23,7 @@ const guideFiles = [
|
|||||||
function documents(language) {
|
function documents(language) {
|
||||||
return [
|
return [
|
||||||
{ entry: `src/${language}/SKILL.md`, to: 'SKILL.md', skill: true },
|
{ entry: `src/${language}/SKILL.md`, to: 'SKILL.md', skill: true },
|
||||||
...agentReferences.map((file) => ({
|
...agentReferences[language].map((file) => ({
|
||||||
entry: `src/${language}/references/${file}`,
|
entry: `src/${language}/references/${file}`,
|
||||||
to: `references/${file}`,
|
to: `references/${file}`,
|
||||||
})),
|
})),
|
||||||
@@ -32,6 +37,20 @@ function guides(language) {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const russianDocumentation = [
|
||||||
|
{ from: '../../README_RU.md', to: 'references/README_RU.md' },
|
||||||
|
{
|
||||||
|
fromDirectory: '../../docs/ru',
|
||||||
|
toDirectory: 'references/docs/ru',
|
||||||
|
extensions: ['.md'],
|
||||||
|
exclude: [
|
||||||
|
'guides/AGENTS.md',
|
||||||
|
'guides/README.md',
|
||||||
|
'reference/README.md',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
{
|
{
|
||||||
name: 'svg-sprites',
|
name: 'svg-sprites',
|
||||||
@@ -43,10 +62,10 @@ export default [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'svg-sprites-ru',
|
name: 'svg-sprites-ru',
|
||||||
description: 'Используй только при настройке, изменении или диагностике @gromlab/svg-sprites. Триггеры: @gromlab/svg-sprites, svg-sprite.config.ts, defineSpriteConfig, generateSprite, standalone, standalone@vite, standalone@webpack, react@vite, react@webpack, next@app, next@pages, SpriteConfig.input, --input, SpriteViewer и --icon-color-N. НЕ используй для самописных SVG-спрайтов, inline SVG, favicon, растровых изображений, icon fonts или выбора библиотеки иконок.',
|
description: 'Используй только при настройке, изменении или диагностике @gromlab/svg-sprites. Триггеры: @gromlab/svg-sprites, svg-sprite.config.json, svg-sprite.config.ts, defineSpriteConfig, generateSprite, standalone, standalone@vite, standalone@webpack, react@vite, react@webpack, next@app, next@pages, SpriteConfig.input, --input, SpriteViewer и --icon-color-N. НЕ используй для самописных SVG-спрайтов, inline SVG, favicon, растровых изображений, icon fonts или выбора библиотеки иконок.',
|
||||||
output: '../artifacts/svg-sprites-ru',
|
output: '../artifacts/svg-sprites-ru',
|
||||||
maxSkillBytes: 48_000,
|
maxSkillBytes: 48_000,
|
||||||
documents: documents('ru'),
|
documents: documents('ru'),
|
||||||
copy: guides('ru'),
|
copy: russianDocumentation,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,19 +1,282 @@
|
|||||||
# @gromlab/svg-sprites
|
# @gromlab/svg-sprites
|
||||||
|
|
||||||
<!-- include: ./core/00-package-overview.md -->
|
## Что делает пакет
|
||||||
<!-- include: ./core/10-mode-selection.md -->
|
|
||||||
<!-- include: ./core/20-project-inspection.md -->
|
|
||||||
<!-- include: ./core/30-react-next-setup.md -->
|
|
||||||
<!-- include: ./core/40-generated-contract.md -->
|
|
||||||
<!-- include: ./core/50-usage-and-colors.md -->
|
|
||||||
<!-- include: ./core/60-verification.md -->
|
|
||||||
<!-- include: ./core/70-diagnostics.md -->
|
|
||||||
|
|
||||||
## Справочники по необходимости
|
`@gromlab/svg-sprites` — CLI-генератор SVG-спрайтов для пользовательских SVG-файлов. Пакет не содержит собственного набора иконок: он собирает SVG проекта во внешний sprite asset, создаёт нативный типизированный Web Component для standalone bundler modes и React-компонент для React/Next.js.
|
||||||
|
|
||||||
- Для статической публикации открой [bare standalone](./references/guides/standalone.md). Для vanilla-приложений со сборщиком открой [standalone + Vite](./references/guides/standalone-vite.md) или [standalone + Webpack](./references/guides/standalone-webpack.md).
|
Пакет рассчитан на несколько независимых спрайтов в одном проекте. Каждый явно выбранный config-файл или config-less каталог описывает один спрайт и получает собственные:
|
||||||
- Для React открой exact guide для [Vite](./references/guides/react-vite.md) или [Webpack](./references/guides/react-webpack.md).
|
|
||||||
- Для Next.js App Router открой exact guide для [Turbopack](./references/guides/next-app-turbopack.md) или [Webpack](./references/guides/next-app-webpack.md).
|
- SVG asset;
|
||||||
- Для Next.js Pages Router открой exact guide для [Turbopack](./references/guides/next-pages-turbopack.md) или [Webpack](./references/guides/next-pages-webpack.md).
|
- mode-specific manifest data;
|
||||||
- Для вызова генератора из Node.js открой [программный API](./references/programmatic-api.md).
|
- для bundler modes — типы имён и production entry `.svg-sprite/index.js`;
|
||||||
- Для gradients, filters, `url(#...)`, нестандартных цветов и проблем с `viewBox` открой [сложные SVG](./references/complex-svg.md).
|
- для `standalone@vite`/`standalone@webpack` — нативный Web Component с явной функцией регистрации;
|
||||||
|
- только для React/Next.js — React-компонент;
|
||||||
|
- для bare `standalone` — deployment-neutral JSON manifest без публичного URL.
|
||||||
|
|
||||||
|
Количество и расположение каталогов определяет проект. Например, `name: 'file-manager'` создаёт `FileManagerIcon`, `FileManagerIconName` и `fileManagerIconNames`, а другой каталог с `name: 'navigation'` создаст отдельный `NavigationIcon`. Это примеры API отдельных спрайтов, а не фиксированные экспорты пакета.
|
||||||
|
|
||||||
|
Generated production runtime и declarations не импортируют `@gromlab/svg-sprites`. Генерация через `npx --yes @gromlab/svg-sprites <path-to-config>` не добавляет package в проект. Устанавливай его как development dependency только для Viewer, package-типов config или программного API.
|
||||||
|
|
||||||
|
## Выбор режима
|
||||||
|
|
||||||
|
Выбери ровно один поддерживаемый mode key:
|
||||||
|
|
||||||
|
| Проект | Mode key |
|
||||||
|
|---|---|
|
||||||
|
| Static HTML / собственная публикация | `standalone` |
|
||||||
|
| Standalone + Vite | `standalone@vite` |
|
||||||
|
| Standalone + Webpack 5 | `standalone@webpack` |
|
||||||
|
| React + Vite | `react@vite` |
|
||||||
|
| React + Webpack 5 | `react@webpack` |
|
||||||
|
| Next.js App Router + Turbopack | `next@app/turbopack` |
|
||||||
|
| Next.js App Router + Webpack 5 | `next@app/webpack` |
|
||||||
|
| Next.js Pages Router + Turbopack | `next@pages/turbopack` |
|
||||||
|
| Next.js Pages Router + Webpack 5 | `next@pages/webpack` |
|
||||||
|
|
||||||
|
Mode задаётся в config, CLI или программном API. Порядок применения: `defaults → config → CLI/API overrides`. После объединения mode обязателен.
|
||||||
|
|
||||||
|
`name` необязателен. Если он не задан, генератор преобразует имя каталога sprite-модуля в kebab-case; для каталогов `svg-sprite` и `svg-sprites` используется имя родительского каталога. Явное `name` должно уже быть записано в kebab-case и начинаться с латинской буквы.
|
||||||
|
|
||||||
|
CLI принимает ровно один путь. Путь к файлу `.ts`, `.js` или `.json` загружает именно этот конфиг независимо от имени. Путь к каталогу включает config-less генерацию, и настройки передаются флагами CLI.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"sprite:<name>": "npx --yes @gromlab/svg-sprites <path-to-config>",
|
||||||
|
"sprite:<name>:cli": "npx --yes @gromlab/svg-sprites --mode <mode-key> <sprite-directory>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Генерация через `npx` не добавляет package в проект. Не используй неполные `react`, `next@app`, `next@pages`, `standalone@` или удалённый `legacy`. Bare `standalone` выбирай только когда приложение само публикует SVG; для Vite/Webpack используй соответствующий полный key. Для нескольких спрайтов создай отдельную команду для каждого config-файла или каталога.
|
||||||
|
|
||||||
|
## Инспекция проекта
|
||||||
|
|
||||||
|
До изменений установи фактический контракт проекта:
|
||||||
|
|
||||||
|
1. Прочитай корневой `package.json`, lock-файл и workspace-конфигурацию; определи framework, bundler и существующие команды.
|
||||||
|
2. Найди config-файлы, команды `svg-sprites` и импорты generated-компонентов. Имя конфига произвольное; ориентируйся на переданный CLI путь и поля объекта.
|
||||||
|
3. Для React определи Vite или Webpack 5 по scripts и конфигу. Для Next.js отдельно определи App/Pages Router и сборщик реальных `dev`/`build` команд.
|
||||||
|
4. Проверь существующие `predev`, `prebuild`, `pretypecheck` и агрегирующие scripts. Не перезаписывай их.
|
||||||
|
5. Для нового спрайта выбери целевой каталог, не навязывая конкретный слой или архитектуру приложения.
|
||||||
|
6. Проверь TypeScript и alias-настройки. Для package subpath exports нужен TypeScript 5+ с `moduleResolution: 'bundler'`, `'node16'` или `'nodenext'`.
|
||||||
|
|
||||||
|
Все input-пути считаются относительно каталога, содержащего явно переданный config-файл; в config-less режиме — относительно переданного каталога. Проверяй `input` как единый контракт:
|
||||||
|
|
||||||
|
- `input?: string | string[]` по умолчанию равен `./icons`;
|
||||||
|
- каждая строка задаёт папку, точный SVG-файл или glob;
|
||||||
|
- папка сканируется плоско; вложенные файлы включаются только явным recursive glob, например `./icons/**/*.svg`;
|
||||||
|
- массив объединяет positive-источники, а элемент с префиксом `!` исключает свои совпадения из общего набора;
|
||||||
|
- каждый positive-источник должен разрешаться хотя бы в один SVG, поэтому отсутствующая или пустая папка, glob без совпадений, отсутствующий файл или точный путь не к SVG являются ошибкой;
|
||||||
|
- разрешённые файлы дедуплицируются и детерминированно сортируются;
|
||||||
|
- разные файлы с одинаковым basename конфликтуют, даже если получены из разных источников.
|
||||||
|
|
||||||
|
Не копируй общий SVG в несколько папок: добавь его точный путь или подходящий glob в `input` каждого нужного спрайта. Используй `**/*.svg` только для намеренного рекурсивного включения.
|
||||||
|
|
||||||
|
## Настройка интеграции
|
||||||
|
|
||||||
|
Не воспроизводи настройку mode по памяти. После инспекции проекта выбери один exact mode и открой соответствующий файл из `references/docs/ru/guides/`. Используй guide как базовый рабочий контракт, затем адаптируй его к существующей структуре проекта.
|
||||||
|
|
||||||
|
Работай в таком порядке:
|
||||||
|
|
||||||
|
1. Определи каталог исходных SVG и каталог одного sprite-модуля. Один config создаёт один независимый спрайт; для нескольких наборов нужны отдельные config-файлы и уникальные `name`.
|
||||||
|
2. Сверь framework, router и bundler с exact mode. Для Next.js проверяй реальные `dev` и `build` scripts, а не только наличие `next.config.*`.
|
||||||
|
3. Предпочитай JSON-конфиг, если проекту не нужны package-типы config. TypeScript-конфиг также загружается через CLI, но установка package нужна, когда он импортирует `defineSpriteConfig` или типы.
|
||||||
|
4. Разрешай все `input` относительно каталога config-файла. Не меняй структуру SVG без необходимости: используй путь к папке, точный файл, glob или массив этих источников.
|
||||||
|
5. Добавь sprite-команду с явным путём к config. Сохрани существующие `dev`, `build`, `typecheck` и lifecycle hooks; встрой генерацию до первого процесса, импортирующего `.svg-sprite`.
|
||||||
|
6. Не запускай одну генерацию дважды через одновременный `predev` и `npm run sprites && ...`. Для нескольких спрайтов создай отдельные команды и один агрегирующий script.
|
||||||
|
7. Если приложение импортирует каталог sprite-модуля, создай пользовательский `index.ts` рядом с `.svg-sprite`; не помещай пользовательские файлы внутрь generated-каталога.
|
||||||
|
8. Выполни первую генерацию до typecheck или запуска приложения, затем проверь mode-specific output и фактический импорт компонента.
|
||||||
|
|
||||||
|
Не добавляй Viewer автоматически. Подключай его только по запросу пользователя или когда нужна визуальная проверка набора, цветов либо сложных SVG. Способ изоляции Viewer от production бери из exact guide: Vite, Webpack, App Router и Pages Router используют разные границы.
|
||||||
|
|
||||||
|
Не копируй snippets между exact modes даже при похожем API. Различаются asset URL, generated-файлы, CSS handling, router boundary и способ подключения debug-инструментов.
|
||||||
|
|
||||||
|
## Контракт generated-каталога
|
||||||
|
|
||||||
|
После генерации React/Next-каталог имеет следующий вид:
|
||||||
|
|
||||||
|
```text
|
||||||
|
svg-sprite/
|
||||||
|
├── icons/ # пользовательские исходники
|
||||||
|
├── svg-sprite.config.json # рекомендуемое имя конфига
|
||||||
|
├── index.ts # необязательный пользовательский barrel
|
||||||
|
├── .gitignore # управляет генератор
|
||||||
|
└── .svg-sprite/
|
||||||
|
├── index.js
|
||||||
|
├── index.d.ts
|
||||||
|
├── icon-data.js
|
||||||
|
├── icon-data.d.ts
|
||||||
|
├── sprite.svg
|
||||||
|
├── svg-sprite.manifest.js
|
||||||
|
├── svg-sprite.manifest.d.ts
|
||||||
|
└── react/
|
||||||
|
├── react-component.js
|
||||||
|
├── react-component.d.ts
|
||||||
|
└── react-component.module.css
|
||||||
|
```
|
||||||
|
|
||||||
|
Standalone не создаёт `react/`. Bare `standalone` генерирует `sprite.svg` и `svg-sprite.manifest.json`; `standalone@vite`/`standalone@webpack` дополнительно генерируют `index.*`, `icon-data.*` и resolved manifest. Их `index.*` также содержит нативный generated Web Component; bare `standalone` не получает JS runtime и не создаёт `.gitignore`.
|
||||||
|
|
||||||
|
Редактируй исходные SVG, config-файл и пользовательский `index.ts`. Не изменяй вручную содержимое `.svg-sprite`: повторная генерация его перезапишет. Во всех modes, кроме bare `standalone`, generated `.gitignore` также находится под управлением генератора. Для импорта из корня sprite-модуля создай barrel:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
export * from './.svg-sprite/index.js'
|
||||||
|
```
|
||||||
|
|
||||||
|
Генератор полностью владеет каталогом `.svg-sprite` и заменяет его при каждом запуске. Никогда не помещай туда пользовательские файлы. Генератор также владеет `.gitignore`, когда выбранный mode его создаёт. Bare `standalone` сохраняет пользовательский `.gitignore`, но удаляет управляемый `.gitignore`, оставшийся после другого mode. Generated-пути не должны содержать symlink.
|
||||||
|
|
||||||
|
В React/Next modes внутренний `index.js` экспортирует компонент из `react/react-component.js` и readonly-массив имён, а `index.d.ts` добавляет props/style-типы и union имени. Standalone bundler modes экспортируют Web Component helpers и типы без `react/`; bare `standalone` не создаёт facade. Manifest declarations bundler modes объявляют типы локально и не импортируют generator package. Manifest содержит mode, target, список и метаданные иконок для debug-инструментов; bundler manifest также содержит URL и не импортируется production-компонентом.
|
||||||
|
|
||||||
|
В bundler modes спрайт остаётся отдельным asset, а SVG path-данные не встраиваются в JavaScript. Content hash зависит от настроек сборщика. Bare `standalone` создаёт файл с фиксированным именем, а приложение само определяет его публичное имя и версионирование:
|
||||||
|
|
||||||
|
- `react@vite` генерирует статический импорт `sprite.svg?no-inline`, запрещающий Vite inline;
|
||||||
|
- `standalone@vite` использует тот же Vite asset-механизм и экспортирует href helper и нативный Web Component без React;
|
||||||
|
- `standalone@webpack` использует Webpack Asset Modules и экспортирует такой же mode-local Web Component без React;
|
||||||
|
- React Webpack 5 и все Next modes получают asset через `new URL(..., import.meta.url).href`, который должен обработать соответствующий сборщик;
|
||||||
|
- кастомный Webpack SVG loader не должен перехватывать generated `sprite.svg`;
|
||||||
|
- в Next mode generated-компонент не содержит `'use client'` и работает в Server Components, SSR и SSG; не добавляй клиентскую границу только ради иконки;
|
||||||
|
- команда сборки Next и mode key должны совпадать: Turbopack с `.../turbopack`, Webpack с `.../webpack`.
|
||||||
|
|
||||||
|
Для bundler modes не перемещай generated sprite в `public` и не переписывай URL вручную. Для bare `standalone` не перемещай managed original: приложение может явно копировать его в deploy output и само отвечает за публичный URL и очистку копии. При смене mode перегенерируй спрайт с новым полным key.
|
||||||
|
|
||||||
|
## Использование, доступность и цвета
|
||||||
|
|
||||||
|
Имя компонента зависит от `name` конкретного спрайта. В `standalone@vite` и `standalone@webpack` значение `name: 'file-manager'` создаёт tag `<file-manager-icon>` и функцию `defineFileManagerIconElement()`:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { defineFileManagerIconElement } from './svg-sprite'
|
||||||
|
|
||||||
|
defineFileManagerIconElement()
|
||||||
|
```
|
||||||
|
|
||||||
|
```html
|
||||||
|
<file-manager-icon icon="folder" aria-hidden="true"></file-manager-icon>
|
||||||
|
```
|
||||||
|
|
||||||
|
Нативный элемент не имеет runtime-зависимостей, сам выбирает generated ID и `viewBox`, получает URL через bundler и рендерит `<svg><use>` в Shadow DOM. Его property `icon` типизирован точным union имён, но строковые HTML attributes проверяются только в runtime. Размер по умолчанию равен `1em × 1em`; меняй его через CSS на host. Bare `standalone` Web Component не генерирует.
|
||||||
|
|
||||||
|
В React/Next.js тот же `name: 'file-manager'` создаёт React-компонент `FileManagerIcon`. Для `name: 'navigation'` используй сгенерированный `NavigationIcon`.
|
||||||
|
|
||||||
|
Импортируй компонент из корня соответствующего каталога спрайта. `width` и `height` не обязательны: размером можно управлять обычным CSS-классом.
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { FileManagerIcon } from './svg-sprite'
|
||||||
|
|
||||||
|
export const OpenButton = () => (
|
||||||
|
<button type="button">
|
||||||
|
<FileManagerIcon icon="folder" className="icon" aria-hidden="true" />
|
||||||
|
<span>Открыть</span>
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
```css
|
||||||
|
.icon {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
color: #4b5563;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`icon` принимает точные имена исходных файлов без `.svg`; неизвестное имя является ошибкой TypeScript. Для небезопасных SVG ID имён генератор хранит публичное имя, но создаёт внутренний стабильный hash ID, поэтому не собирай fragment URL из имени вручную.
|
||||||
|
|
||||||
|
По умолчанию компонент рендерит `<svg>` и принимает стандартные SVG attributes: необязательные `width`/`height`, `className`, `style`, `role`, `aria-*` и обработчики. С `wrapped={true}` корнем становится `<span>`, props относятся к span, а внутренний SVG занимает размер wrapper.
|
||||||
|
|
||||||
|
Generated-компонент не выбирает семантику за приложение и не добавляет `title`. Для декоративной иконки передай `aria-hidden="true"`; для самостоятельной смысловой иконки передай `role="img"` и доступное имя через `aria-label`. Не дублируй имя, если соседний текст уже озвучивает действие. Интерактивность размещай на `button` или `a`, а не на самой иконке.
|
||||||
|
|
||||||
|
Трансформации `removeSize`, `replaceColors` и `addTransition` включены по умолчанию. Для монохромной иконки единственный цвет получает fallback `currentColor`, поэтому управляй CSS-свойством `color`. Для многоцветной передавай типизированные custom properties:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
<FileManagerIcon
|
||||||
|
icon="folder"
|
||||||
|
style={{
|
||||||
|
'--icon-color-1': '#4b5563',
|
||||||
|
'--icon-color-2': '#14b8a6',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
Автозамена рассчитана на `fill`/`stroke` attributes и inline `style`. Значения `none`, `transparent`, `inherit`, `unset`, `initial` не заменяются. CSS-классы и внешние stylesheets, gradients, patterns, filters и `url(#...)` проверяй на реальном результате. Переменные страницы работают через `<svg><use>`, но не проникают во внешний документ при `<img>` или `background-image`; CSS mask оставляет только одноцветный силуэт.
|
||||||
|
|
||||||
|
`SpriteViewer` необязателен. Установи `@gromlab/svg-sprites` как development dependency, только если проекту нужен Viewer. Он принимает manifests или статически обнаружимые loaders, показывает поиск, темы, цвета и примеры, но production-компоненты от него не зависят.
|
||||||
|
|
||||||
|
Перед подключением Viewer открой exact guide. Vite использует отдельную HTML entry, обычный Webpack — development-only entry, Next.js — debug route, а App Router дополнительно требует отдельную Client Component boundary. Не переноси способ подключения между modes.
|
||||||
|
|
||||||
|
## Проверка результата
|
||||||
|
|
||||||
|
После изменения конфига или SVG выполни обязательные проверки:
|
||||||
|
|
||||||
|
1. Запусти точную sprite-команду. Процесс должен завершиться с кодом `0` и сообщить имя, число иконок, mode и каталог `.svg-sprite`.
|
||||||
|
2. Проверь output выбранного exact mode:
|
||||||
|
- bare `standalone` создаёт `sprite.svg` и `svg-sprite.manifest.json`;
|
||||||
|
- `standalone@vite` и `standalone@webpack` дополнительно создают `index.*`, `icon-data.*` и JS manifest, но не каталог `react/`;
|
||||||
|
- React и Next.js modes также создают `react/react-component.js`, declaration и CSS Module.
|
||||||
|
3. Для modes с public facade проверь `.svg-sprite/index.js`, соседний `index.d.ts`, список имён и фактический импорт через пользовательский barrel.
|
||||||
|
4. Проверь manifest: mode и target должны соответствовать выбранному adapter, а список иконок — исходным SVG. В bundler modes URL должен формироваться mode-specific способом; bare JSON manifest намеренно не содержит публичного `spriteUrl`.
|
||||||
|
5. Запусти существующий typecheck проекта, если mode создаёт типы или изменился пользовательский TypeScript-код.
|
||||||
|
6. Запусти минимальную команду приложения, затронутую изменением: `dev`, build или специализированную проверку проекта.
|
||||||
|
|
||||||
|
Не запускай полную production-сборку только ради проверки нового имени иконки. Она нужна, если менялся bundler target, router, Webpack loader, asset URL, deployment path или диагностируется production-only ошибка.
|
||||||
|
|
||||||
|
Визуальную проверку, Network и accessibility tree выполняй только при наличии запущенного приложения и браузерных инструментов. Если таких инструментов нет, не утверждай, что цвета, темы, доступность или HTTP-ответ asset проверены; явно укажи непроверенную часть.
|
||||||
|
|
||||||
|
Viewer используй для сложных цветов, transforms и массовой визуальной проверки. Не добавляй debug route ради обычной генерации одного спрайта.
|
||||||
|
|
||||||
|
## Диагностика
|
||||||
|
|
||||||
|
Сопоставь симптом с проверкой и исправляй первопричину:
|
||||||
|
|
||||||
|
| Симптом | Вероятная причина | Действие |
|
||||||
|
|---|---|---|
|
||||||
|
| `Missing sprite config file or module directory` | Не передан позиционный путь | Передай один config-файл либо каталог для config-less запуска. |
|
||||||
|
| `Expected one config file or module directory` | Передано несколько путей | Создай отдельную команду на каждый спрайт и объедини scripts. |
|
||||||
|
| `Sprite mode is required` | Mode отсутствует и в config, и в CLI | Добавь `mode` в объект или передай полный `--mode`. |
|
||||||
|
| `Unsupported sprite config extension` | Передан файл не `.ts`, `.js` или `.json` | Используй поддерживаемый формат config-файла. |
|
||||||
|
| Positive input-источник не нашёл SVG | Папка отсутствует или пуста, glob не совпал либо точный путь отсутствует или ведёт не к SVG | Разреши источник от каталога конфига и исправь `input`; каждый positive-элемент должен дать хотя бы один SVG. |
|
||||||
|
| Иконки из подпапки не появились | От папки ожидалось рекурсивное сканирование | Используй явный glob, например `./icons/**/*.svg`; папки сканируются плоско. |
|
||||||
|
| Исключённая иконка всё ещё присутствует | У исключения нет префикса `!`, оно находится не в массиве `input` или считается не от того каталога | Добавь совпадающий `!`-элемент и считай его от каталога конфига. |
|
||||||
|
| CLI выбрал не все источники | Несколько источников поместили в одно значение `--input` или пропустили option | Повтори `--input <path-or-glob>` отдельно для каждого источника или исключения. |
|
||||||
|
| Конфликт имени иконки или SVG ID | Два разных файла имеют одинаковый basename либо hash-ID столкнулся с именем | Переименуй один исходный SVG; не выбирай файл неявно. |
|
||||||
|
| `Refusing to overwrite a user file` | В корне sprite-модуля уже есть пользовательский `.gitignore`, который mode должен создать | Не перезаписывай файл: выбери другой sprite-каталог или согласуй перенос существующего `.gitignore`. |
|
||||||
|
| Нет `.svg-sprite/index.js` или имя отсутствует в autocomplete | Для bare `standalone` это ожидаемо; в остальных modes генерация не запускалась, barrel неверен либо type server держит старый модуль | Сверь exact mode, запусти sprite-команду, проверь `export * from './.svg-sprite/index.js'`, затем typecheck; при необходимости перезапусти TypeScript server. |
|
||||||
|
| SVG не загружается или URL неверен | Mode не совпадает со сборщиком, неверен Webpack `publicPath` либо кастомный loader перехватил asset | Сверь mode и build-команду, проверь Asset Modules/`publicPath`, исключи generated SVG из несовместимого loader. |
|
||||||
|
| Next build расходится между SSR и браузером | Модуль сгенерирован для другого bundler/router или URL переписан вручную | Верни generated `new URL(...)`, выбери точный Next mode и перегенерируй. |
|
||||||
|
| `color` не меняет многоцветную иконку | У иконки несколько переменных или она показана через `<img>`/CSS background | Используй `<FileManagerIcon>`/`<svg><use>` и нужные `--icon-color-N`. |
|
||||||
|
| Gradient/filter выглядит неверно | Автозамена цветов не гарантирует сложные paint servers | Изучи generated SVG; при необходимости отключи `replaceColors` для спрайта или упрости источник. |
|
||||||
|
| Viewer пуст | Manifest не создан, loader не обнаружен сборщиком или неверна Client Component boundary | Сначала сгенерируй спрайт, затем сверь manifest import и способ подключения с exact guide; в App Router оставь `'use client'` только в компоненте Viewer. |
|
||||||
|
|
||||||
|
При неизвестной ошибке зафиксируй полную CLI-команду, mode, путь к config-файлу или каталогу и первый stack/error message. Затем минимально воспроизведи проблему на одном спрайте, не удаляя пользовательские файлы и управляемый `.gitignore`.
|
||||||
|
|
||||||
|
## Карта reference-документации
|
||||||
|
|
||||||
|
References являются частью собранного skill. Открывай только документы, относящиеся к текущей задаче, но перед изменением интеграции exact-mode guide обязателен.
|
||||||
|
|
||||||
|
### Обзор
|
||||||
|
|
||||||
|
- [README пакета](./references/README_RU.md) — возможности, основной React/Next.js сценарий и ссылки на документацию.
|
||||||
|
|
||||||
|
### Конфигурация
|
||||||
|
|
||||||
|
- [Конфигурация](./references/docs/ru/configuration.md) — JSON, JavaScript, TypeScript, поля config, `input` и запуск CLI.
|
||||||
|
|
||||||
|
### Exact-mode guides
|
||||||
|
|
||||||
|
- [`standalone`](./references/docs/ru/guides/standalone.md) — static HTML и собственная публикация SVG.
|
||||||
|
- [`standalone@vite`](./references/docs/ru/guides/standalone-vite.md) — vanilla-приложение с Vite и Web Component.
|
||||||
|
- [`standalone@webpack`](./references/docs/ru/guides/standalone-webpack.md) — vanilla-приложение с Webpack 5 и Web Component.
|
||||||
|
- [`react@vite`](./references/docs/ru/guides/react-vite.md) — React с Vite.
|
||||||
|
- [`react@webpack`](./references/docs/ru/guides/react-webpack.md) — React с Webpack 5.
|
||||||
|
- [`next@app/turbopack`](./references/docs/ru/guides/next-app-turbopack.md) — Next.js App Router с Turbopack.
|
||||||
|
- [`next@app/webpack`](./references/docs/ru/guides/next-app-webpack.md) — Next.js App Router с Webpack.
|
||||||
|
- [`next@pages/turbopack`](./references/docs/ru/guides/next-pages-turbopack.md) — Next.js Pages Router с Turbopack.
|
||||||
|
- [`next@pages/webpack`](./references/docs/ru/guides/next-pages-webpack.md) — Next.js Pages Router с Webpack.
|
||||||
|
|
||||||
|
### Технические справочники
|
||||||
|
|
||||||
|
- [Технический справочник](./references/docs/ru/reference/technical.md) — requirements, CLI, naming, generated API, assets, transforms, цвета, Viewer, Git, CI и диагностика.
|
||||||
|
- [Программный API](./references/docs/ru/reference/programmatic-api.md) — `generateSprite`, overrides, config API, compiler и Viewer runtime.
|
||||||
|
|
||||||
|
### Agent-specific reference
|
||||||
|
|
||||||
|
- [Сложные SVG](./references/complex-svg.md) — gradients, patterns, filters, masks, `url(#...)`, `viewBox`, fragment IDs и визуальная диагностика.
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
## Что делает пакет
|
|
||||||
|
|
||||||
`@gromlab/svg-sprites` — CLI-генератор SVG-спрайтов для пользовательских SVG-файлов. Пакет не содержит собственного набора иконок: он собирает SVG проекта во внешний sprite asset, создаёт нативный типизированный Web Component для standalone bundler modes и React-компонент для React/Next.js.
|
|
||||||
|
|
||||||
Пакет рассчитан на несколько независимых спрайтов в одном проекте. Каждый явно выбранный config-файл или config-less каталог описывает один спрайт и получает собственные:
|
|
||||||
|
|
||||||
- SVG asset;
|
|
||||||
- mode-specific manifest data;
|
|
||||||
- для bundler modes — типы имён и production entry `.svg-sprite/index.js`;
|
|
||||||
- для `standalone@vite`/`standalone@webpack` — нативный Web Component с явной функцией регистрации;
|
|
||||||
- только для React/Next.js — React-компонент;
|
|
||||||
- для bare `standalone` — deployment-neutral JSON manifest без публичного URL.
|
|
||||||
|
|
||||||
Количество и расположение каталогов определяет проект. Например, `name: 'file-manager'` создаёт `FileManagerIcon`, а другой каталог с `name: 'navigation'` создаст отдельный `NavigationIcon`. Имена `FileManagerIcon` и `fileManagerIconNames` ниже являются примерами API одного из возможных спрайтов, а не фиксированными экспортами пакета.
|
|
||||||
|
|
||||||
Generated production runtime и declarations не импортируют `@gromlab/svg-sprites`. Генерация работает через `npx --package` с зафиксированной версией без добавления package в проект. Устанавливай его как development dependency только для Viewer, package-типов config или программного API.
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
## Выбор режима
|
|
||||||
|
|
||||||
Выбери ровно один поддерживаемый mode key:
|
|
||||||
|
|
||||||
| Проект | Mode key |
|
|
||||||
|---|---|
|
|
||||||
| Static HTML / собственная публикация | `standalone` |
|
|
||||||
| Standalone + Vite | `standalone@vite` |
|
|
||||||
| Standalone + Webpack 5 | `standalone@webpack` |
|
|
||||||
| React + Vite | `react@vite` |
|
|
||||||
| React + Webpack 5 | `react@webpack` |
|
|
||||||
| Next.js App Router + Turbopack | `next@app/turbopack` |
|
|
||||||
| Next.js App Router + Webpack 5 | `next@app/webpack` |
|
|
||||||
| Next.js Pages Router + Turbopack | `next@pages/turbopack` |
|
|
||||||
| Next.js Pages Router + Webpack 5 | `next@pages/webpack` |
|
|
||||||
|
|
||||||
Mode задаётся в config, CLI или программном API. Порядок применения: `defaults → config → CLI/API overrides`. После объединения mode обязателен.
|
|
||||||
|
|
||||||
CLI принимает ровно один путь. Путь к файлу `.ts`, `.js` или `.json` загружает именно этот конфиг независимо от имени. Путь к каталогу включает config-less генерацию, и настройки передаются флагами CLI.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"scripts": {
|
|
||||||
"sprite:<name>": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites <path-to-config>",
|
|
||||||
"sprite:<name>:cli": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites --mode <mode-key> <sprite-directory>"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Генерация через `npx` не добавляет package в проект. В CI укажи точную версию вместо `latest`. Не используй неполные `react`, `next@app`, `next@pages`, `standalone@` или удалённый `legacy`. Bare `standalone` выбирай только когда приложение само публикует SVG; для Vite/Webpack используй соответствующий полный key. Для нескольких спрайтов создай отдельную команду для каждого config-файла или каталога.
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
## Инспекция проекта
|
|
||||||
|
|
||||||
До изменений установи фактический контракт проекта:
|
|
||||||
|
|
||||||
1. Прочитай корневой `package.json`, lock-файл и workspace-конфигурацию; определи framework, bundler и существующие команды.
|
|
||||||
2. Найди config-файлы, команды `svg-sprites` и импорты generated-компонентов. Имя конфига произвольное; ориентируйся на переданный CLI путь и поля объекта.
|
|
||||||
3. Для React определи Vite или Webpack 5 по scripts и конфигу. Для Next.js отдельно определи App/Pages Router и сборщик реальных `dev`/`build` команд.
|
|
||||||
4. Проверь существующие `predev`, `prebuild`, `pretypecheck` и агрегирующие scripts. Не перезаписывай их.
|
|
||||||
5. Для нового спрайта выбери целевой каталог, не навязывая конкретный слой или архитектуру приложения.
|
|
||||||
6. Проверь TypeScript и alias-настройки. Для package subpath exports нужен TypeScript 5+ с `moduleResolution: 'bundler'`, `'node16'` или `'nodenext'`.
|
|
||||||
|
|
||||||
Все input-пути считаются относительно каталога, содержащего явно переданный config-файл; в config-less режиме — относительно переданного каталога. Проверяй `input` как единый контракт:
|
|
||||||
|
|
||||||
- `input?: string | string[]` по умолчанию равен `./icons`;
|
|
||||||
- каждая строка задаёт папку, точный SVG-файл или glob;
|
|
||||||
- папка сканируется плоско; вложенные файлы включаются только явным recursive glob, например `./icons/**/*.svg`;
|
|
||||||
- массив объединяет positive-источники, а элемент с префиксом `!` исключает свои совпадения из общего набора;
|
|
||||||
- каждый positive-источник должен разрешаться хотя бы в один SVG, поэтому отсутствующая или пустая папка, glob без совпадений, отсутствующий файл или точный путь не к SVG являются ошибкой;
|
|
||||||
- разрешённые файлы дедуплицируются и детерминированно сортируются;
|
|
||||||
- разные файлы с одинаковым basename конфликтуют, даже если получены из разных источников.
|
|
||||||
|
|
||||||
Не копируй общий SVG в несколько папок: добавь его точный путь или подходящий glob в `input` каждого нужного спрайта. Используй `**/*.svg` только для намеренного рекурсивного включения.
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
## Настройка React или Next.js
|
|
||||||
|
|
||||||
Выбери целевой каталог для одного спрайта. Он может находиться рядом с feature, в общем каталоге иконок или в любом другом месте, принятом в проекте. Следующая структура является только примером:
|
|
||||||
|
|
||||||
```text
|
|
||||||
src/ui/file-manager/svg-sprite/
|
|
||||||
├── icons/
|
|
||||||
│ ├── check.svg
|
|
||||||
│ └── folder.svg
|
|
||||||
└── svg-sprite.config.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
Один `svg-sprite.config.ts` создаёт один независимый спрайт. Для нескольких наборов выбери несколько каталогов и дай каждому уникальное `name`.
|
|
||||||
|
|
||||||
Генератор не нужно устанавливать в проект. Начни с plain config без package
|
|
||||||
import:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export default {
|
|
||||||
mode: 'react@vite',
|
|
||||||
name: 'file-manager',
|
|
||||||
description: 'Иконки файлового менеджера',
|
|
||||||
input: ['./icons', '../../shared/icons/close.svg'],
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
`input` принимает одну папку, точный SVG-файл или glob либо массив, объединяющий эти источники. Элемент массива с префиксом `!` исключает совпадения. Папки сканируются плоско; для рекурсии нужен явный glob `**/*.svg`. Если `input` не задан, используется `./icons`. Все пути считаются от каталога конфига, и каждый positive-источник должен найти хотя бы один SVG.
|
|
||||||
|
|
||||||
Контракт объекта одинаков для React и Next.js; отличается полный `mode`. Устанавливай package только для необязательного Viewer, программного API или package-типизации config. В exact guides также есть локальный copy-paste type для проектов без package.
|
|
||||||
|
|
||||||
`name` должен начинаться с латинской буквы и записываться в kebab-case; из примера `file-manager` будут созданы `FileManagerIcon`, `FileManagerIconName` и `fileManagerIconNames`. Другой спрайт получает собственные имена. Если `name` не задан, генератор выводит его из каталога.
|
|
||||||
|
|
||||||
Добавь отдельную команду с выбранным mode key и одним путём:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"scripts": {
|
|
||||||
"sprite:file-manager": "npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts",
|
|
||||||
"sprites": "npm run sprite:file-manager"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Для Next.js укажи в config полный ключ, например `next@app/turbopack`. Для нескольких спрайтов добавь по команде `sprite:<name>` на каждый config-файл и последовательно вызови их из `sprites`.
|
|
||||||
|
|
||||||
Чтобы задать источники через CLI, повторяй `--input <path-or-glob>`; значения образуют тот же массивный контракт, включая исключения с `!`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npx --yes --package=@gromlab/svg-sprites@latest svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts \
|
|
||||||
--input ./icons \
|
|
||||||
--input '../../shared/icons/**/*.svg' \
|
|
||||||
--input '!../../shared/icons/legacy-*.svg'
|
|
||||||
```
|
|
||||||
|
|
||||||
Generated-файлы в `.svg-sprite` по умолчанию исключаются из Git, поэтому запускай `sprites` до процессов, которым нужны компонент, типы или asset. Если проект импортирует корень sprite-модуля, создай пользовательский `index.ts` с `export * from './.svg-sprite'`. Generated declarations self-contained и не импортируют generator package.
|
|
||||||
|
|
||||||
Запускай генерацию либо через `predev`/`prebuild`/`pretypecheck`, либо явно внутри соответствующих команд. Не используй обе формы для одной команды, иначе генерация выполнится дважды. Сохраняй существующие команды и не создавай второй одноимённый JSON key.
|
|
||||||
|
|
||||||
Запусти первую генерацию вручную:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run sprites
|
|
||||||
```
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
## Контракт generated-каталога
|
|
||||||
|
|
||||||
После генерации React/Next-каталог имеет следующий вид:
|
|
||||||
|
|
||||||
```text
|
|
||||||
svg-sprite/
|
|
||||||
├── icons/ # пользовательские исходники
|
|
||||||
├── svg-sprite.config.ts # рекомендуемое имя конфига
|
|
||||||
├── index.ts # необязательный пользовательский barrel
|
|
||||||
├── .gitignore # управляет генератор
|
|
||||||
└── .svg-sprite/
|
|
||||||
├── index.js
|
|
||||||
├── index.d.ts
|
|
||||||
├── icon-data.js
|
|
||||||
├── icon-data.d.ts
|
|
||||||
├── sprite.svg
|
|
||||||
├── svg-sprite.manifest.js
|
|
||||||
├── svg-sprite.manifest.d.ts
|
|
||||||
└── react/
|
|
||||||
├── react-component.js
|
|
||||||
├── react-component.d.ts
|
|
||||||
└── react-component.module.css
|
|
||||||
```
|
|
||||||
|
|
||||||
Standalone не создаёт `react/`. Bare `standalone` генерирует `sprite.svg` и
|
|
||||||
`svg-sprite.manifest.json`; `standalone@vite`/`standalone@webpack` дополнительно
|
|
||||||
генерируют `index.*`, `icon-data.*` и resolved manifest. Их `index.*` также
|
|
||||||
содержит нативный generated Web Component; bare `standalone` не получает JS runtime,
|
|
||||||
не создаёт и не изменяет `.gitignore`.
|
|
||||||
|
|
||||||
Редактируй исходные SVG, config-файл и пользовательский `index.ts`. Не изменяй вручную содержимое `.svg-sprite`: повторная генерация его перезапишет. Во всех modes, кроме bare `standalone`, generated `.gitignore` также находится под управлением генератора. Для импорта из корня sprite-модуля создай barrel:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
export * from './.svg-sprite'
|
|
||||||
```
|
|
||||||
|
|
||||||
Генератор полностью владеет каталогом `.svg-sprite` и заменяет его при каждом запуске. Никогда не помещай туда пользовательские файлы. Генератор также владеет `.gitignore`, когда выбранный mode его создаёт; bare `standalone` оставляет существующий `.gitignore` без изменений. Generated-пути не должны содержать symlink.
|
|
||||||
|
|
||||||
Внутренний `index.js` экспортирует компонент из `react/react-component.js` и readonly-массив имён; соседний `index.d.ts` добавляет props/style-типы и union имени. Manifest declarations bundler modes объявляют типы локально и не импортируют generator package. Manifest содержит mode, URL, target, список и метаданные иконок для debug-инструментов и не импортируется production-компонентом.
|
|
||||||
|
|
||||||
Спрайт остаётся отдельным asset с content hash; SVG path-данные не встраиваются в JavaScript:
|
|
||||||
|
|
||||||
- `react@vite` генерирует статический импорт `sprite.svg?no-inline`, запрещающий Vite inline;
|
|
||||||
- `standalone@vite` использует тот же Vite asset-механизм и экспортирует href helper и нативный Web Component без React;
|
|
||||||
- `standalone@webpack` использует Webpack Asset Modules и экспортирует такой же mode-local Web Component без React;
|
|
||||||
- React Webpack 5 и все Next modes генерируют `new URL('./sprite.svg', import.meta.url).href`, который должен обработать Asset Modules соответствующего сборщика;
|
|
||||||
- кастомный Webpack SVG loader не должен перехватывать generated `sprite.svg`;
|
|
||||||
- в Next mode generated-компонент не содержит `'use client'` и работает в Server Components, SSR и SSG; не добавляй клиентскую границу только ради иконки;
|
|
||||||
- команда сборки Next и mode key должны совпадать: Turbopack с `.../turbopack`, Webpack с `.../webpack`.
|
|
||||||
|
|
||||||
Для bundler modes не перемещай generated sprite в `public` и не переписывай URL вручную. Для bare `standalone` не перемещай managed original: приложение может явно копировать его в deploy output и само отвечает за публичный URL и очистку копии. При смене mode перегенерируй спрайт с новым полным key.
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
## Использование, доступность и цвета
|
|
||||||
|
|
||||||
Имя компонента зависит от `name` конкретного спрайта. В `standalone@vite` и `standalone@webpack` значение `name: 'file-manager'` создаёт tag `<file-manager-icon>` и функцию `defineFileManagerIconElement()`:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { defineFileManagerIconElement } from './svg-sprite'
|
|
||||||
|
|
||||||
defineFileManagerIconElement()
|
|
||||||
```
|
|
||||||
|
|
||||||
```html
|
|
||||||
<file-manager-icon icon="folder" aria-hidden="true"></file-manager-icon>
|
|
||||||
```
|
|
||||||
|
|
||||||
Нативный элемент не имеет runtime-зависимостей, сам выбирает generated ID и `viewBox`, получает URL через bundler и рендерит `<svg><use>` в Shadow DOM. Его property `icon` типизирован точным union имён, но строковые HTML attributes проверяются только в runtime. Размер по умолчанию равен `1em × 1em`; меняй его через CSS на host. Bare `standalone` Web Component не генерирует.
|
|
||||||
|
|
||||||
В React/Next.js тот же `name: 'file-manager'` создаёт React-компонент `FileManagerIcon`. Для `name: 'navigation'` используй сгенерированный `NavigationIcon`.
|
|
||||||
|
|
||||||
Импортируй компонент из корня соответствующего каталога спрайта. `width` и `height` не обязательны: размером можно управлять обычным CSS-классом.
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
import { FileManagerIcon } from './svg-sprite'
|
|
||||||
|
|
||||||
export const OpenButton = () => (
|
|
||||||
<button type="button">
|
|
||||||
<FileManagerIcon icon="folder" className="icon" aria-hidden="true" />
|
|
||||||
<span>Открыть</span>
|
|
||||||
</button>
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
```css
|
|
||||||
.icon {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
color: #4b5563;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
`icon` принимает точные имена исходных файлов без `.svg`; неизвестное имя является ошибкой TypeScript. Для небезопасных SVG ID имён генератор хранит публичное имя, но создаёт внутренний стабильный hash ID, поэтому не собирай fragment URL из имени вручную.
|
|
||||||
|
|
||||||
По умолчанию компонент рендерит `<svg>` и принимает стандартные SVG attributes: необязательные `width`/`height`, `className`, `style`, `role`, `aria-*` и обработчики. С `wrapped={true}` корнем становится `<span>`, props относятся к span, а внутренний SVG занимает размер wrapper. Это удобно, когда размер и цвета полностью задаются классом:
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
<FileManagerIcon
|
|
||||||
icon="check"
|
|
||||||
wrapped
|
|
||||||
className="statusIcon"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
```
|
|
||||||
|
|
||||||
```css
|
|
||||||
.statusIcon {
|
|
||||||
width: 1.5rem;
|
|
||||||
height: 1.5rem;
|
|
||||||
color: currentColor;
|
|
||||||
--icon-color-1: #4b5563;
|
|
||||||
--icon-color-2: #14b8a6;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Generated-компонент не выбирает семантику за приложение и не добавляет `title`. Для декоративной иконки передай `aria-hidden="true"`; для самостоятельной смысловой иконки передай `role="img"` и доступное имя через `aria-label`. Не дублируй имя, если соседний текст уже озвучивает действие. Интерактивность размещай на `button` или `a`, а не на самой иконке.
|
|
||||||
|
|
||||||
Трансформации `removeSize`, `replaceColors` и `addTransition` включены по умолчанию. Для монохромной иконки единственный цвет получает fallback `currentColor`, поэтому управляй CSS-свойством `color`. Для многоцветной передавай типизированные custom properties:
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
<FileManagerIcon
|
|
||||||
icon="folder"
|
|
||||||
wrapped
|
|
||||||
className="folderIcon"
|
|
||||||
style={{
|
|
||||||
'--icon-color-1': '#4b5563',
|
|
||||||
'--icon-color-2': '#14b8a6',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
```
|
|
||||||
|
|
||||||
Автозамена рассчитана на `fill`/`stroke` attributes и inline `style`. Значения `none`, `transparent`, `inherit`, `unset`, `initial` не заменяются. CSS-классы и внешние stylesheets, gradients, patterns, filters и `url(#...)` проверяй на реальном результате. Переменные страницы работают через `<svg><use>`, но не проникают во внешний документ при `<img>` или `background-image`; CSS mask оставляет только одноцветный силуэт.
|
|
||||||
|
|
||||||
`SpriteViewer` необязателен. Установи `@gromlab/svg-sprites` как development dependency, только если проекту нужен Viewer, и подключай его из `@gromlab/svg-sprites/react` на debug-маршруте:
|
|
||||||
|
|
||||||
- в Vite передай результат строкового literal `import.meta.glob('/src/**/svg-sprite/.svg-sprite/svg-sprite.manifest.js')`;
|
|
||||||
- в Webpack передай массив статических `() => import('.../.svg-sprite/svg-sprite.manifest.js')`;
|
|
||||||
- в Next.js используй такие же статические loaders, а для App Router помести Viewer в отдельный файл с `'use client'`.
|
|
||||||
|
|
||||||
Viewer принимает manifests/loaders, показывает поиск, темы, цвета и примеры, но production-компоненты от него не зависят.
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
## Проверка результата
|
|
||||||
|
|
||||||
После изменения конфига или SVG выполни обязательные быстрые проверки:
|
|
||||||
|
|
||||||
1. Запусти точную sprite-команду, например `npm run sprite:file-manager`; процесс должен завершиться с кодом `0` и сообщить имя, число иконок, mode и каталог `.svg-sprite`.
|
|
||||||
2. Проверь наличие `.svg-sprite/index.js`, `.svg-sprite/index.d.ts`, `sprite.svg`, пары `icon-data.js`/`.d.ts`, manifest `.js`/`.d.ts`, `react/react-component.js`, его `.d.ts` и CSS Module.
|
|
||||||
3. Убедись, что новая иконка присутствует в readonly-массиве имён и принимается prop `icon`.
|
|
||||||
4. Запусти существующую проверку типов проекта, например `npm run typecheck`.
|
|
||||||
5. Проверь в `.svg-sprite/svg-sprite.manifest.js`, что `target` совпадает с выбранным mode key; generated asset expression должен быть `?no-inline` для Vite и `new URL(...)` для Webpack/Next.
|
|
||||||
|
|
||||||
Не запускай полную production-сборку только ради проверки изменения списка иконок. Она нужна, если менялся bundler target, конфигурация asset pipeline, Next router/bundler, Webpack loader или диагностируется ошибка URL в runtime.
|
|
||||||
|
|
||||||
Визуальную проверку, Network и accessibility tree выполняй только при наличии запущенного приложения и браузерных инструментов. Если таких инструментов нет, не утверждай, что цвета, темы, доступность или HTTP-ответ asset проверены; явно укажи непроверенную часть.
|
|
||||||
|
|
||||||
`SpriteViewer` также необязателен. Используй его для сложных цветов, transforms и массовой визуальной проверки, но не добавляй debug route ради обычной генерации одного спрайта.
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
## Диагностика
|
|
||||||
|
|
||||||
Сопоставь симптом с проверкой и исправляй первопричину:
|
|
||||||
|
|
||||||
| Симптом | Вероятная причина | Действие |
|
|
||||||
|---|---|---|
|
|
||||||
| `Missing sprite config file or module directory` | Не передан позиционный путь | Передай один config-файл либо каталог для config-less запуска. |
|
|
||||||
| `Expected one config file or module directory` | Передано несколько путей | Создай отдельную команду на каждый спрайт и объедини scripts. |
|
|
||||||
| `Sprite mode is required` | Mode отсутствует и в config, и в CLI | Добавь `mode` в объект или передай полный `--mode`. |
|
|
||||||
| `Unsupported sprite config extension` | Передан файл не `.ts`, `.js` или `.json` | Используй поддерживаемый формат config-файла. |
|
|
||||||
| Positive input-источник не нашёл SVG | Папка отсутствует или пуста, glob не совпал либо точный путь отсутствует или ведёт не к SVG | Разреши источник от каталога конфига и исправь `input`; каждый positive-элемент должен дать хотя бы один SVG. |
|
|
||||||
| Иконки из подпапки не появились | От папки ожидалось рекурсивное сканирование | Используй явный glob, например `./icons/**/*.svg`; папки сканируются плоско. |
|
|
||||||
| Исключённая иконка всё ещё присутствует | У исключения нет префикса `!`, оно находится не в массиве `input` или считается не от того каталога | Добавь совпадающий `!`-элемент и считай его от каталога конфига. |
|
|
||||||
| CLI выбрал не все источники | Несколько источников поместили в одно значение `--input` или пропустили option | Повтори `--input <path-or-glob>` отдельно для каждого источника или исключения. |
|
|
||||||
| Конфликт имени иконки или SVG ID | Два разных файла имеют одинаковый basename либо hash-ID столкнулся с именем | Переименуй один исходный SVG; не выбирай файл неявно. |
|
|
||||||
| `Refusing to overwrite/delete a user file` | Пользовательский файл занял managed-путь или потерял marker | Не обходи защиту: перенеси файл либо выбери другой sprite-каталог и перегенерируй. |
|
|
||||||
| Нет `.svg-sprite/index.js` или имя отсутствует в autocomplete | Генерация не запускалась после изменения, пользовательский barrel не экспортирует `.svg-sprite` либо type server держит старый модуль | Запусти sprite-команду, проверь `export * from './.svg-sprite'`, затем typecheck; при необходимости перезапусти TypeScript server. |
|
|
||||||
| SVG не загружается или URL неверен | Mode не совпадает со сборщиком, неверен Webpack `publicPath` либо кастомный loader перехватил asset | Сверь mode и build-команду, проверь Asset Modules/`publicPath`, исключи generated SVG из несовместимого loader. |
|
|
||||||
| Next build расходится между SSR и браузером | Модуль сгенерирован для другого bundler/router или URL переписан вручную | Верни generated `new URL(...)`, выбери точный Next mode и перегенерируй. |
|
|
||||||
| `color` не меняет многоцветную иконку | У иконки несколько переменных или она показана через `<img>`/CSS background | Используй `<FileManagerIcon>`/`<svg><use>` и нужные `--icon-color-N`. |
|
|
||||||
| Gradient/filter выглядит неверно | Автозамена цветов не гарантирует сложные paint servers | Изучи generated SVG; при необходимости отключи `replaceColors` для спрайта или упрости источник. |
|
|
||||||
| Viewer пуст | Манифесты не созданы, glob/import не статический или неверен Client Component boundary | Сначала сгенерируй спрайты; для Vite используй literal glob, для Webpack/Next статические loaders, для App Router добавь `'use client'` только Viewer-странице. |
|
|
||||||
|
|
||||||
При неизвестной ошибке зафиксируй полную CLI-команду, mode, путь к config-файлу или каталогу и первый stack/error message. Затем минимально воспроизведи проблему на одном спрайте, не удаляя пользовательские файлы и защитные markers.
|
|
||||||
@@ -173,4 +173,4 @@ External stack fragment support и поведение paint servers могут
|
|||||||
- Ручной fragment не работает для имени с пробелом: используй ID из manifest.
|
- Ручной fragment не работает для имени с пробелом: используй ID из manifest.
|
||||||
- Один сложный icon требует иных transforms: вынеси его в отдельный sprite; per-icon transform config отсутствует.
|
- Один сложный icon требует иных transforms: вынеси его в отдельный sprite; per-icon transform config отсутствует.
|
||||||
|
|
||||||
Target-specific запуск и проверка описаны в exact-mode файлах каталога [guides](guides/standalone.md).
|
Target-specific запуск и проверка описаны в exact-mode файлах каталога [guides](docs/ru/guides/standalone.md).
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
# Программный API: операционный reference
|
|
||||||
|
|
||||||
Используй `generateSprite(source, overrides?)` как основной Node.js API.
|
|
||||||
|
|
||||||
## Из config-файла
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { generateSprite } from '@gromlab/svg-sprites'
|
|
||||||
|
|
||||||
await generateSprite('src/ui/icons/svg-sprite.config.ts')
|
|
||||||
```
|
|
||||||
|
|
||||||
`source` должен указывать на конкретный `.ts`, `.js` или `.json` файл. Имя файла произвольное; генератор не выполняет discovery. Корнем sprite-модуля и базой относительных путей становится каталог этого файла.
|
|
||||||
|
|
||||||
## Без config-файла
|
|
||||||
|
|
||||||
```ts
|
|
||||||
await generateSprite('src/ui/icons', {
|
|
||||||
mode: 'react@vite',
|
|
||||||
name: 'app',
|
|
||||||
input: './icons',
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Каталог включает config-less режим. После объединения настроек `mode` обязателен.
|
|
||||||
|
|
||||||
`input?: string | string[]` по умолчанию равен `./icons`. Каждое значение задаёт папку, точный SVG-файл или glob и считается от каталога конфига либо config-less source-каталога. Папка сканируется плоско; рекурсия включается только явным glob, например `./icons/**/*.svg`. Массив объединяет источники, а элементы с префиксом `!` исключают совпадения. Каждый positive-элемент должен разрешаться хотя бы в один SVG. Итоговые файлы дедуплицируются и сортируются, а разные файлы с одинаковым basename вызывают ошибку.
|
|
||||||
|
|
||||||
## Overrides
|
|
||||||
|
|
||||||
```ts
|
|
||||||
await generateSprite('src/ui/icons/custom.json', {
|
|
||||||
mode: 'react@webpack',
|
|
||||||
input: [
|
|
||||||
'../../shared/icons/**/*.svg',
|
|
||||||
'!../../shared/icons/legacy-*.svg',
|
|
||||||
],
|
|
||||||
transform: { addTransition: false },
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Порядок: `defaults → config → API overrides`. `transform` объединяется по отдельным полям; переданный `input` заменяет значение из config.
|
|
||||||
|
|
||||||
Специализированные `generateReactSprite` и `generateNextSprite` оставлены как совместимые обёртки, но для нового кода предпочитай `generateSprite`.
|
|
||||||
|
|
||||||
Для загрузки и собственной оркестрации доступны `loadSpriteConfig`, `validateSpriteConfig`, `resolveSpriteConfig`, `compileSpriteContent` и `createShapeTransform`.
|
|
||||||
@@ -19,7 +19,12 @@ const guideModes = new Map([
|
|||||||
|
|
||||||
const sectionHeadings = {
|
const sectionHeadings = {
|
||||||
en: ['Generate the sprite', 'Debug and preview', 'Type the config'],
|
en: ['Generate the sprite', 'Debug and preview', 'Type the config'],
|
||||||
ru: ['Генерация спрайта', 'Дебаг и превью', 'Типизация конфига'],
|
ru: ['Генерация спрайта', 'Дебаг и превью'],
|
||||||
|
}
|
||||||
|
|
||||||
|
const commandPatterns = {
|
||||||
|
en: /npx --yes (?:--package=@gromlab\/svg-sprites@latest svg-sprites|@gromlab\/svg-sprites@latest)/,
|
||||||
|
ru: /npx --yes @gromlab\/svg-sprites(?:\s|$)/,
|
||||||
}
|
}
|
||||||
|
|
||||||
function markdownFiles(directory) {
|
function markdownFiles(directory) {
|
||||||
@@ -46,15 +51,26 @@ test('exact-mode guides are reusable by docs and skills', () => {
|
|||||||
heading.replace(/^## (?:\d+\. )?/, '')
|
heading.replace(/^## (?:\d+\. )?/, '')
|
||||||
))
|
))
|
||||||
assert.deepEqual(headings, sectionHeadings[language])
|
assert.deepEqual(headings, sectionHeadings[language])
|
||||||
assert.match(source, /npx --yes (?:--package=@gromlab\/svg-sprites@latest svg-sprites|@gromlab\/svg-sprites@latest)/)
|
assert.match(source, commandPatterns[language])
|
||||||
assert.match(source, /npm install --save-dev @gromlab\/svg-sprites/)
|
if (language !== 'ru' || mode !== 'standalone') {
|
||||||
assert.match(source, new RegExp(`mode: '${mode.replaceAll('/', '\\/')}'`))
|
assert.match(source, /npm install --save-dev @gromlab\/svg-sprites/)
|
||||||
|
}
|
||||||
|
if (language === 'ru') {
|
||||||
|
assert.doesNotMatch(source, /npx --yes[^\n]*@gromlab\/svg-sprites@/)
|
||||||
|
}
|
||||||
|
const modePattern = language === 'ru'
|
||||||
|
? new RegExp(`"mode": "${mode.replaceAll('/', '\\/')}"`)
|
||||||
|
: new RegExp(`mode: '${mode.replaceAll('/', '\\/')}'`)
|
||||||
|
assert.match(source, modePattern)
|
||||||
assert.doesNotMatch(source, /\]\([^)]+\)/, `${language}/${file} must not depend on its location`)
|
assert.doesNotMatch(source, /\]\([^)]+\)/, `${language}/${file} must not depend on its location`)
|
||||||
|
|
||||||
const generation = source.indexOf(sectionHeadings[language][0])
|
const generation = source.indexOf(sectionHeadings[language][0])
|
||||||
const preview = source.indexOf(sectionHeadings[language][1])
|
const preview = source.indexOf(sectionHeadings[language][1])
|
||||||
const typing = source.indexOf(sectionHeadings[language][2])
|
assert.ok(generation < preview)
|
||||||
assert.ok(generation < preview && preview < typing)
|
if (language === 'en') {
|
||||||
|
const typing = source.indexOf(sectionHeadings[language][2])
|
||||||
|
assert.ok(preview < typing)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user