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:
@@ -1,6 +1,6 @@
|
||||
# AI skills
|
||||
|
||||
Исходники английского и русского скиллов находятся в `skills/svg-sprites/src/{en,ru}/`. Готовые переносимые артефакты генерируются в `skills/artifacts/`, игнорируются Git и упаковываются в ZIP во время release workflow.
|
||||
Исходники обязательного контекста английского и русского skills находятся в `skills/svg-sprites/src/{en,ru}/`. Канонические exact-mode guides находятся в `docs/{en,ru}/guides/` и копируются в соответствующий skill без изменения. Готовые переносимые артефакты генерируются в `skills/artifacts/`, игнорируются Git и упаковываются в ZIP во время release workflow.
|
||||
|
||||
Обе языковые версии имеют одинаковую структуру:
|
||||
|
||||
@@ -17,15 +17,11 @@ src/<language>/
|
||||
│ ├── 60-verification.md
|
||||
│ └── 70-diagnostics.md
|
||||
└── references/
|
||||
├── react-vite.md
|
||||
├── react-webpack.md
|
||||
├── next-app.md
|
||||
├── next-pages.md
|
||||
├── programmatic-api.md
|
||||
└── complex-svg.md
|
||||
```
|
||||
|
||||
`core/` содержит обязательные знания, раскрываемые прямо в итоговый `SKILL.md`. `references/` содержит самостоятельные инструкции для агента по конкретным стекам и редким сценариям. Пользовательские `README*.md` и `docs/{en,ru}/*.md` дополнительно копируются в `references/upstream/` как вторичный источник полного публичного API.
|
||||
`core/` содержит обязательные знания, раскрываемые прямо в итоговый `SKILL.md`. Локальный `references/` содержит только agent-specific материалы. Девять файлов из `docs/<language>/guides/` копируются в `references/guides/`; английский artifact получает только английские guides, русский только русские. Второй набор mode guides и каталог `references/upstream/` не создаются.
|
||||
|
||||
## Композиция Markdown
|
||||
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
const agentReferences = [
|
||||
'react-vite.md',
|
||||
'react-webpack.md',
|
||||
'next-app.md',
|
||||
'next-pages.md',
|
||||
'programmatic-api.md',
|
||||
'complex-svg.md',
|
||||
]
|
||||
|
||||
const guideFiles = [
|
||||
'standalone.md',
|
||||
'standalone-vite.md',
|
||||
'standalone-webpack.md',
|
||||
'react-vite.md',
|
||||
'react-webpack.md',
|
||||
'next-app-turbopack.md',
|
||||
'next-app-webpack.md',
|
||||
'next-pages-turbopack.md',
|
||||
'next-pages-webpack.md',
|
||||
]
|
||||
|
||||
function documents(language) {
|
||||
return [
|
||||
{ entry: `src/${language}/SKILL.md`, to: 'SKILL.md', skill: true },
|
||||
@@ -17,36 +25,28 @@ function documents(language) {
|
||||
]
|
||||
}
|
||||
|
||||
const upstream = [
|
||||
{ from: '../../README.md', to: 'references/upstream/README.md' },
|
||||
{ from: '../../README_RU.md', to: 'references/upstream/README_RU.md' },
|
||||
{
|
||||
fromDirectory: '../../docs/en',
|
||||
toDirectory: 'references/upstream/docs/en',
|
||||
extensions: ['.md'],
|
||||
},
|
||||
{
|
||||
fromDirectory: '../../docs/ru',
|
||||
toDirectory: 'references/upstream/docs/ru',
|
||||
extensions: ['.md'],
|
||||
},
|
||||
]
|
||||
function guides(language) {
|
||||
return guideFiles.map((file) => ({
|
||||
from: `../../docs/${language}/guides/${file}`,
|
||||
to: `references/guides/${file}`,
|
||||
}))
|
||||
}
|
||||
|
||||
export default [
|
||||
{
|
||||
name: 'svg-sprites',
|
||||
description: 'Use only when configuring, generating, or troubleshooting @gromlab/svg-sprites. Triggers: @gromlab/svg-sprites, svg-sprite.config.ts, defineSpriteConfig, generateSprite, standalone, standalone@vite, standalone@webpack, react@vite, react@webpack, next@app, next@pages, inputFiles, SpriteViewer, or --icon-color-N. Do NOT use for custom SVG sprites, favicons, raster images, icon fonts, choosing an icon set, or inline SVG without this package.',
|
||||
description: 'Use only when configuring, generating, or troubleshooting @gromlab/svg-sprites. Triggers: @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, or --icon-color-N. Do NOT use for custom SVG sprites, favicons, raster images, icon fonts, choosing an icon set, or inline SVG without this package.',
|
||||
output: '../artifacts/svg-sprites',
|
||||
maxSkillBytes: 48_000,
|
||||
documents: documents('en'),
|
||||
copy: upstream,
|
||||
copy: guides('en'),
|
||||
},
|
||||
{
|
||||
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, inputFiles, SpriteViewer и --icon-color-N. НЕ используй для самописных SVG-спрайтов, inline SVG, favicon, растровых изображений, icon fonts или выбора библиотеки иконок.',
|
||||
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 или выбора библиотеки иконок.',
|
||||
output: '../artifacts/svg-sprites-ru',
|
||||
maxSkillBytes: 48_000,
|
||||
documents: documents('ru'),
|
||||
copy: upstream,
|
||||
copy: guides('ru'),
|
||||
},
|
||||
]
|
||||
|
||||
@@ -11,8 +11,9 @@
|
||||
|
||||
## References to open as needed
|
||||
|
||||
- For React + Vite, open [React + Vite](./references/react-vite.md); for React with a custom Webpack 5 setup, open [React + Webpack](./references/react-webpack.md).
|
||||
- For Next.js, open the guide for the [App Router](./references/next-app.md) or [Pages Router](./references/next-pages.md), then select the section for the bundler actually in use.
|
||||
- For static publishing, open [bare standalone](./references/guides/standalone.md). For vanilla bundler apps, open [standalone + Vite](./references/guides/standalone-vite.md) or [standalone + Webpack](./references/guides/standalone-webpack.md).
|
||||
- For React, open the exact [Vite](./references/guides/react-vite.md) or [Webpack](./references/guides/react-webpack.md) guide.
|
||||
- For the Next.js App Router, open the exact [Turbopack](./references/guides/next-app-turbopack.md) or [Webpack](./references/guides/next-app-webpack.md) guide.
|
||||
- For the Next.js Pages Router, open the exact [Turbopack](./references/guides/next-pages-turbopack.md) or [Webpack](./references/guides/next-pages-webpack.md) guide.
|
||||
- To invoke the generator from Node.js, open the [programmatic API](./references/programmatic-api.md).
|
||||
- For gradients, filters, `url(#...)`, unusual colors, and `viewBox` issues, open [complex SVGs](./references/complex-svg.md).
|
||||
- Use the complete user documentation as a secondary source: [package README](./references/upstream/README.md).
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
## What the package does
|
||||
|
||||
`@gromlab/svg-sprites` is a CLI generator that builds SVG sprites from user-provided SVG files. The package does not include its own icon set: it compiles the project's SVGs into an external sprite asset, supports standalone projects, and creates typed components for React/Next.js.
|
||||
`@gromlab/svg-sprites` is a CLI generator that builds SVG sprites from user-provided SVG files. The package does not include its own icon set: it compiles the project's SVGs into an external sprite asset, creates a native typed Web Component for standalone bundler modes, and creates a React component for React/Next.js.
|
||||
|
||||
The package supports multiple independent sprites in one project. Each explicitly selected config file or config-less directory describes one sprite and gets its own:
|
||||
|
||||
- SVG asset;
|
||||
- mode-specific manifest data;
|
||||
- icon name types and `.svg-sprite/index.js` for bundler modes;
|
||||
- a native Web Component with an explicit registration function for `standalone@vite`/`standalone@webpack`;
|
||||
- a React component only for React/Next.js;
|
||||
- a deployment-neutral JSON manifest without a public URL for bare `standalone`.
|
||||
|
||||
The project determines how many sprite directories exist and where they live. For example, `name: 'file-manager'` produces `FileManagerIcon`, while another directory with `name: 'navigation'` produces a separate `NavigationIcon`. The names `FileManagerIcon` and `fileManagerIconNames` used below are examples of the API for one possible sprite, not fixed package exports.
|
||||
|
||||
Generated production runtime does not import `@gromlab/svg-sprites` at runtime. Install the package as a development dependency so configuration helpers and the local CLI use the version recorded in the project's lockfile.
|
||||
Generated production runtime and declarations do not import `@gromlab/svg-sprites`. Generation can run through a pinned `npx --package` command without adding the package to the project. Install it as a development dependency only for the Viewer, package-provided config types, or the programmatic API.
|
||||
|
||||
@@ -21,10 +21,10 @@ The CLI accepts exactly one path. A `.ts`, `.js`, or `.json` file loads that exa
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"sprite:<name>": "svg-sprites <path-to-config>",
|
||||
"sprite:<name>:cli": "svg-sprites --mode <mode-key> <sprite-directory>"
|
||||
"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>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Do not use incomplete `react`, `next@app`, `next@pages`, or `standalone@` keys, or the removed `legacy` mode. Use bare `standalone` only when the application publishes the SVG itself; use the complete Vite/Webpack key otherwise. Create one command per config file or directory when the project has multiple sprites.
|
||||
Generation through `npx` does not add the package to the project. Pin an exact package version instead of `latest` in CI. Do not use incomplete `react`, `next@app`, `next@pages`, or `standalone@` keys, or the removed `legacy` mode. Use bare `standalone` only when the application publishes the SVG itself; use the complete Vite/Webpack key otherwise. Create one command per config file or directory when the project has multiple sprites.
|
||||
|
||||
@@ -9,14 +9,14 @@ Establish the project's actual contract before making changes:
|
||||
5. For a new sprite, choose a target directory without imposing a particular application layer or architecture.
|
||||
6. Check TypeScript and alias settings. Package subpath exports require TypeScript 5+ with `moduleResolution: 'bundler'`, `'node16'`, or `'nodenext'`.
|
||||
|
||||
All config paths are relative to the directory containing the explicitly selected config file; in config-less mode they are relative to the supplied directory:
|
||||
All input paths are relative to the directory containing the explicitly selected config file; in config-less mode they are relative to the supplied directory. Inspect `input` using this single contract:
|
||||
|
||||
- `inputFolder` defaults to `./icons`;
|
||||
- `inputFiles` contains additional relative paths to individual SVGs and is merged with `inputFolder`;
|
||||
- the same absolute file is deduplicated, but different files with the same icon basename cause an error;
|
||||
- scanning `inputFolder` is shallow: only immediate files ending in `.svg` are read, and nested directories are not traversed;
|
||||
- an explicitly configured `inputFolder` that does not exist is an error;
|
||||
- if `inputFolder` is omitted, `./icons` does not exist, and `inputFiles` is non-empty, generation uses only `inputFiles`;
|
||||
- an empty final input set, a missing file, or a path that does not point to an `.svg` file is an error.
|
||||
- `input?: string | string[]` defaults to `./icons`;
|
||||
- each string is a folder, an exact SVG file, or a glob;
|
||||
- a folder is scanned shallowly; nested files are included only by an explicit recursive glob such as `./icons/**/*.svg`;
|
||||
- an array combines positive sources, while an item prefixed with `!` excludes its matches from the combined set;
|
||||
- every positive source must resolve to at least one SVG, so a missing or empty folder, an unmatched glob, a missing file, or a non-SVG exact file is an error;
|
||||
- resolved files are deduplicated and sorted deterministically;
|
||||
- different files with the same basename are a conflict, even when they came from different sources.
|
||||
|
||||
Do not copy a shared SVG into several folders: add its relative path to `inputFiles` in every sprite that needs it. If a recursive structure is required, list the files explicitly or reorganize the sources; the generator does not perform recursive scanning.
|
||||
Do not copy a shared SVG into several folders: add its exact path or a suitable glob to `input` in every sprite that needs it. Use `**/*.svg` only when recursive inclusion is intentional.
|
||||
|
||||
@@ -12,27 +12,21 @@ src/ui/file-manager/svg-sprite/
|
||||
|
||||
One `svg-sprite.config.ts` creates one independent sprite. For multiple sets, choose multiple directories and assign each a unique `name`.
|
||||
|
||||
Install the package as a development dependency:
|
||||
|
||||
```bash
|
||||
npm install --save-dev @gromlab/svg-sprites
|
||||
```
|
||||
|
||||
Use the configuration helper for autocomplete and type checking:
|
||||
The generator does not need to be installed in the project. Start with a plain
|
||||
config that has no package import:
|
||||
|
||||
```ts
|
||||
import { defineSpriteConfig } from '@gromlab/svg-sprites'
|
||||
|
||||
export default defineSpriteConfig({
|
||||
export default {
|
||||
mode: 'react@vite',
|
||||
name: 'file-manager',
|
||||
description: 'File manager icons',
|
||||
inputFolder: './icons',
|
||||
inputFiles: ['../../shared/icons/close.svg'],
|
||||
})
|
||||
input: ['./icons', '../../shared/icons/close.svg'],
|
||||
}
|
||||
```
|
||||
|
||||
The object contract is the same for React and Next.js; only the full `mode` differs.
|
||||
`input` accepts one folder, exact SVG file, or glob, or an array that combines them. Prefix an array item with `!` to exclude matches. Folders are shallow; use an explicit `**/*.svg` glob for recursion. Omit `input` to use `./icons`. Every path is resolved from the config directory, and every positive source must match at least one SVG.
|
||||
|
||||
The object contract is the same for React and Next.js; only the full `mode` differs. Install the package only for the optional Viewer, programmatic API, or package-provided config typing. Exact guides also provide a local copy-paste config type for projects that remain package-free.
|
||||
|
||||
`name` must begin with an ASCII letter and use kebab-case. The example `file-manager` produces `FileManagerIcon`, `FileManagerIconName`, and `fileManagerIconNames`. Another sprite gets its own names. If `name` is omitted, the generator derives it from the directory.
|
||||
|
||||
@@ -41,7 +35,7 @@ Add a separate command with the selected mode key and one path:
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
@@ -49,9 +43,18 @@ Add a separate command with the selected mode key and one path:
|
||||
|
||||
For Next.js, set the full key in the config, for example `next@app/turbopack`. For multiple sprites, add one `sprite:<name>` command per config file and invoke them sequentially from `sprites`.
|
||||
|
||||
Generated files in `.svg-sprite` are excluded from Git by default, so run `sprites` before any process that needs the component, types, or asset. If the project imports the sprite-module root, create a user-owned `index.ts` with `export * from './.svg-sprite'`. Add generation to `predev`, `prebuild`, and, when a `typecheck` script exists, `pretypecheck`.
|
||||
To select sources from the CLI instead, repeat `--input <path-or-glob>`; the values form the same array contract, including `!` exclusions:
|
||||
|
||||
If a lifecycle script is missing, create it. If it already exists, preserve its command and append generation with `&&`; for example, change `"prebuild": "npm run lint"` to `"prebuild": "npm run lint && npm run sprites"`. Never replace an existing `pre*` script with generation alone, and never create a duplicate JSON key.
|
||||
```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 files in `.svg-sprite` are excluded from Git by default, so run `sprites` before any process that needs the component, types, or asset. If the project imports the sprite-module root, create a user-owned `index.ts` with `export * from './.svg-sprite'`. Generated declarations are self-contained and do not import the generator package.
|
||||
|
||||
Run generation either from `predev`/`prebuild`/`pretypecheck` hooks or explicitly inside the corresponding commands. Do not use both forms for the same command, or generation runs twice. Preserve existing script commands and never create a duplicate JSON key.
|
||||
|
||||
Run the first generation manually:
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ svg-sprite/
|
||||
├── index.ts # optional user-owned barrel
|
||||
├── .gitignore # managed by the generator
|
||||
└── .svg-sprite/
|
||||
├── state.json # ownership registry and contract version
|
||||
├── index.js
|
||||
├── index.d.ts
|
||||
├── icon-data.js
|
||||
@@ -25,23 +24,25 @@ svg-sprite/
|
||||
|
||||
Standalone does not create `react/`. Bare `standalone` generates `sprite.svg` and
|
||||
`svg-sprite.manifest.json`; `standalone@vite`/`standalone@webpack` additionally
|
||||
generate `index.*`, `icon-data.*`, and a resolved manifest.
|
||||
generate `index.*`, `icon-data.*`, and a resolved manifest. Their `index.*` also
|
||||
contains a native generated Web Component; bare `standalone` gets no JavaScript runtime
|
||||
and does not create or modify `.gitignore`.
|
||||
|
||||
Edit the source SVGs, selected config, and user-owned `index.ts`. Do not manually change `.gitignore` or anything in `.svg-sprite`: the next generation will overwrite them. To import from the sprite-module root, create a barrel:
|
||||
Edit the source SVGs, selected config, and user-owned `index.ts`. Do not manually change anything in `.svg-sprite`: the next generation will overwrite it. In modes other than bare `standalone`, the generated `.gitignore` is also managed and must not be edited manually. To import from the sprite-module root, create a barrel:
|
||||
|
||||
```ts
|
||||
export * from './.svg-sprite'
|
||||
```
|
||||
|
||||
The generator owns `.gitignore` and files inside `.svg-sprite`. The `state.json` registry allows stale generated files to be removed, but the writer refuses to overwrite or delete any file without a generated marker. Do not remove the marker, bypass the refusal, or replace generated paths with symlinks: move the user-owned file or choose a different directory.
|
||||
The generator owns the complete `.svg-sprite` directory and replaces it on every run. Never put user files inside it. The generator also owns `.gitignore` when the selected mode creates it; bare `standalone` leaves an existing `.gitignore` untouched. Generated paths must not contain symlinks.
|
||||
|
||||
The internal `index.js` exports the component from `react/react-component.js` and the readonly name array; the adjacent `index.d.ts` adds props/style types and the icon-name union. The manifest contains the mode, URL, target, icon list, and icon metadata for debug tools and is not imported by the production component.
|
||||
The internal `index.js` exports the component from `react/react-component.js` and the readonly name array; the adjacent `index.d.ts` adds props/style types and the icon-name union. Bundler-mode manifest declarations define their types locally and do not import the generator package. The manifest contains the mode, URL, target, icon list, and icon metadata for debug tools and is not imported by the production component.
|
||||
|
||||
The sprite remains a separate content-hashed asset; SVG path data is not embedded in JavaScript:
|
||||
|
||||
- `react@vite` generates a static `sprite.svg?no-inline` import, preventing Vite from inlining it;
|
||||
- `standalone@vite` uses the same Vite asset mechanism but exports an href helper without React;
|
||||
- `standalone@webpack` uses Webpack Asset Modules without React;
|
||||
- `standalone@vite` uses the same Vite asset mechanism and exports an href helper plus a native Web Component without React;
|
||||
- `standalone@webpack` uses Webpack Asset Modules and exports the same mode-local Web Component without React;
|
||||
- React Webpack 5 and all Next modes generate `new URL('./sprite.svg', import.meta.url).href`, which must be processed by the selected bundler's Asset Modules;
|
||||
- a custom Webpack SVG loader must not intercept the generated `sprite.svg`;
|
||||
- in Next mode, the generated component does not contain `'use client'` and works in Server Components, SSR, and SSG; do not add a client boundary solely for an icon;
|
||||
|
||||
@@ -1,6 +1,20 @@
|
||||
## Usage, accessibility, and colors
|
||||
|
||||
The component name depends on the specific sprite's `name`. These examples use `name: 'file-manager'`, so the generated component is called `FileManagerIcon`. For `name: 'navigation'`, use the generated `NavigationIcon`.
|
||||
The component name depends on the specific sprite's `name`. In `standalone@vite` and `standalone@webpack`, `name: 'file-manager'` creates the `<file-manager-icon>` tag and the `defineFileManagerIconElement()` function:
|
||||
|
||||
```ts
|
||||
import { defineFileManagerIconElement } from './svg-sprite'
|
||||
|
||||
defineFileManagerIconElement()
|
||||
```
|
||||
|
||||
```html
|
||||
<file-manager-icon icon="folder" aria-hidden="true"></file-manager-icon>
|
||||
```
|
||||
|
||||
The native element has no runtime dependencies, selects the generated ID and `viewBox`, obtains the URL through the bundler, and renders `<svg><use>` in Shadow DOM. Its `icon` property is typed with the exact name union, while plain HTML attribute values are validated only at runtime. It defaults to `1em × 1em`; resize the host with CSS. Bare `standalone` does not generate a Web Component.
|
||||
|
||||
In React/Next.js, the same `name: 'file-manager'` creates the `FileManagerIcon` React component. For `name: 'navigation'`, use the generated `NavigationIcon`.
|
||||
|
||||
Import the component from the root of its sprite directory. `width` and `height` are optional: ordinary CSS classes can control the size.
|
||||
|
||||
@@ -64,10 +78,10 @@ The `removeSize`, `replaceColors`, and `addTransition` transforms are enabled by
|
||||
|
||||
Automatic replacement targets `fill`/`stroke` attributes and inline `style`. The values `none`, `transparent`, `inherit`, `unset`, and `initial` are not replaced. Check CSS classes and external stylesheets, gradients, patterns, filters, and `url(#...)` against the actual output. Page variables work through `<svg><use>`, but do not cross into an external document loaded through `<img>` or `background-image`; a CSS mask preserves only a monochrome silhouette.
|
||||
|
||||
`SpriteViewer` is optional. Import it from `@gromlab/svg-sprites/react` only on a debug route:
|
||||
`SpriteViewer` is optional. Install `@gromlab/svg-sprites` as a development dependency only when the project needs the Viewer, then import it from `@gromlab/svg-sprites/react` on a debug route:
|
||||
|
||||
- in Vite, pass the result of a string-literal `import.meta.glob('/src/**/svg-sprite/.svg-sprite/svg-sprite.manifest.js')`;
|
||||
- in Webpack, pass an array of static `() => import('.../manifest')` loaders;
|
||||
- in Webpack, pass an array of static `() => import('.../.svg-sprite/svg-sprite.manifest.js')` loaders;
|
||||
- in Next.js, use the same static loaders, and for the App Router put the Viewer in a separate file with `'use client'`.
|
||||
|
||||
The Viewer accepts manifests/loaders and provides search, themes, colors, and examples, but production components do not depend on it. Import it from the package already installed as a development dependency.
|
||||
The Viewer accepts manifests/loaders and provides search, themes, colors, and examples, but production components do not depend on it.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
After changing a config or SVG, perform these required quick checks:
|
||||
|
||||
1. Run the exact sprite command, for example `npm run sprite:file-manager`; it must exit with code `0` and report the name, icon count, mode, and `.svg-sprite` directory.
|
||||
2. Confirm that `.svg-sprite/index.js`, `.svg-sprite/index.d.ts`, `sprite.svg`, the `icon-data.js`/`.d.ts` and manifest `.js`/`.d.ts` pairs, `react/react-component.js`, its `.d.ts` and CSS Module, and `state.json` exist.
|
||||
2. Confirm that `.svg-sprite/index.js`, `.svg-sprite/index.d.ts`, `sprite.svg`, the `icon-data.js`/`.d.ts` and manifest `.js`/`.d.ts` pairs, `react/react-component.js`, its `.d.ts` and CSS Module exist.
|
||||
3. Confirm that the new icon appears in the readonly name array and is accepted by the `icon` prop.
|
||||
4. Run the project's existing type check, for example `npm run typecheck`.
|
||||
5. Check that `target` in `.svg-sprite/svg-sprite.manifest.js` matches the selected mode key; the generated asset expression must use `?no-inline` for Vite and `new URL(...)` for Webpack/Next.
|
||||
|
||||
@@ -8,9 +8,10 @@ Match the symptom to the relevant check and fix the root cause:
|
||||
| `Expected one config file or module directory` | Multiple paths were passed | Create one command per sprite and combine the scripts. |
|
||||
| `Sprite mode is required` | Mode is absent from both config and CLI | Add `mode` to the object or pass the full `--mode`. |
|
||||
| `Unsupported sprite config extension` | The supplied file is not `.ts`, `.js`, or `.json` | Use a supported config format. |
|
||||
| `Input directory does not exist` | The relative path is wrong or an explicitly configured folder is missing | Resolve it from the config directory; create the folder or correct `inputFolder`. |
|
||||
| Icons from a subdirectory are missing | Recursive scanning was assumed | Move the SVG to the top level of `inputFolder` or list it in `inputFiles`. |
|
||||
| `SVG file does not exist`, `File is not an SVG`, or an empty input set | Invalid `inputFiles`, extension, or sources | Correct the path/extension and provide at least one input SVG. |
|
||||
| A positive input source has no SVG matches | A folder is missing or empty, a glob matches nothing, or an exact path is missing or not an SVG | Resolve the source from the config directory and correct `input`; every positive item must produce at least one SVG. |
|
||||
| Icons from a subdirectory are missing | A folder source was expected to scan recursively | Use an explicit glob such as `./icons/**/*.svg`; folders are shallow. |
|
||||
| An excluded icon is still present | The exclusion lacks a leading `!`, is not in the `input` array, or is relative to the wrong directory | Add a matching `!` item and resolve it from the config directory. |
|
||||
| CLI source selection is incomplete | Multiple sources were packed into one `--input` value or an option was omitted | Repeat `--input <path-or-glob>` once per source or exclusion. |
|
||||
| Icon name or SVG ID collision | Two different files have the same basename, or a hash ID collides with a name | Rename one source SVG; do not select a file implicitly. |
|
||||
| `Refusing to overwrite/delete a user file` | A user file occupies a managed path or lost its marker | Do not bypass the protection: move the file or choose another sprite directory and regenerate. |
|
||||
| Missing `.svg-sprite/index.js` or name absent from autocomplete | Generation did not run, the user barrel does not export `.svg-sprite`, or the type server cached an old module | Run the sprite command, check `export * from './.svg-sprite'`, then typecheck; restart the TypeScript server if necessary. |
|
||||
|
||||
@@ -131,7 +131,7 @@ It is preserved as the fragment ID. Other names, such as `folder open.svg` or `2
|
||||
|
||||
Do not manually construct `#folder open`. Use the generated component or `.svg-sprite/svg-sprite.manifest.js`, which records both `name` and the actual `id`.
|
||||
|
||||
Different files with the same basename are forbidden, even from different directories. Rename one source meaningfully; `inputFiles` order does not select a winner.
|
||||
Different files with the same basename are forbidden, even from different directories. Rename one source meaningfully; source order or overlap never selects a winner.
|
||||
|
||||
## Rendering method
|
||||
|
||||
@@ -173,4 +173,4 @@ External stack-fragment support and paint-server behavior can vary across browse
|
||||
- A manual fragment fails for a name containing spaces: use the ID from the manifest.
|
||||
- One complex icon requires different transforms: move it to a separate sprite; per-icon transform config is not supported.
|
||||
|
||||
Target-specific execution and verification are documented in [react-vite.md](react-vite.md), [react-webpack.md](react-webpack.md), [next-app.md](next-app.md), and [next-pages.md](next-pages.md).
|
||||
Target-specific execution and verification are documented in the exact-mode files under [guides](guides/standalone.md).
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
# Next.js App Router: operational reference
|
||||
|
||||
## When to use this reference
|
||||
|
||||
Use this document when the application uses `app/` or `src/app/` and needs a generated component for Server Components, SSR, or SSG. First determine the actual bundler: Turbopack and Webpack use different targets. For `pages/`, use [next-pages.md](next-pages.md).
|
||||
|
||||
## Target matrix
|
||||
|
||||
| Bundler | CLI mode |
|
||||
|---|---|
|
||||
| Turbopack | `next@app/turbopack` |
|
||||
| Webpack 5 | `next@app/webpack` |
|
||||
|
||||
Do not infer the target only from the presence of `next.config.*`. Check the actual flags in `dev`/`build`. The generator mode and verification-build bundler must agree.
|
||||
|
||||
## Prepare the sprite directory
|
||||
|
||||
```text
|
||||
src/ui/file-manager/svg-sprite/
|
||||
├── icons/
|
||||
│ ├── check.svg
|
||||
│ └── folder.svg
|
||||
└── svg-sprite.config.ts
|
||||
```
|
||||
|
||||
Install the package as a development dependency:
|
||||
|
||||
```bash
|
||||
npm install --save-dev @gromlab/svg-sprites
|
||||
```
|
||||
|
||||
```ts
|
||||
import { defineSpriteConfig } from '@gromlab/svg-sprites'
|
||||
|
||||
export default defineSpriteConfig({
|
||||
mode: 'next@app/turbopack',
|
||||
name: 'file-manager',
|
||||
description: 'File manager icons',
|
||||
inputFolder: './icons',
|
||||
inputFiles: ['../../../../shared/icons/check.svg'],
|
||||
})
|
||||
```
|
||||
|
||||
- The project chooses the directory for each specific sprite; it does not have to be a module/feature directory. Each config describes one of potentially many application sprites.
|
||||
- `svg-sprite.config.ts` is the recommended name; the CLI accepts an explicitly selected `.ts`, `.js`, or `.json` file with any name.
|
||||
- Paths are relative to its directory; `inputFolder` scanning is shallow.
|
||||
- The local folder and `inputFiles` are merged. Equal basenames from different files are forbidden.
|
||||
- If no name is configured, it is derived from the directory; for a directory named `svg-sprite`, the parent directory name is used. An explicit `name` is more stable when files move.
|
||||
- `name` must use kebab-case and begin with a letter.
|
||||
- `FileManagerIcon` below is only an example generated name for `name: 'file-manager'`.
|
||||
- The Next preset always generates `stack` and includes a root sprite `viewBox`; this is not a user setting.
|
||||
|
||||
## Generation
|
||||
|
||||
Example scripts for Turbopack:
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts",
|
||||
"predev": "npm run sprite:file-manager",
|
||||
"prebuild": "npm run sprite:file-manager",
|
||||
"pretypecheck": "npm run sprite:file-manager"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For Webpack, replace only the complete mode with `next@app/webpack`. Do not run both targets sequentially for one directory: the second generation overwrites the first target's files. Run `npm run sprite:file-manager` for the first generation.
|
||||
|
||||
## Server Component
|
||||
|
||||
The generated production component does not contain `'use client'`. Import it directly into a server `page.tsx` or `layout.tsx`:
|
||||
|
||||
```tsx
|
||||
import { FileManagerIcon } from '@/ui/file-manager/svg-sprite'
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<main>
|
||||
<FileManagerIcon icon="folder" className="icon" aria-label="Folder" />
|
||||
</main>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
`width` and `height` are optional in JSX; set the size with a CSS class or `wrapped`. Do not add a Client Component boundary solely for the icon. The generated module builds the asset URL with static `new URL('./sprite.svg', import.meta.url).href`; the same mechanism is used during SSR and in the browser.
|
||||
|
||||
Do not move the SVG into `public`. The generator rewrites `.svg-sprite` and `.gitignore`; the root `index.ts` is application-owned and may contain `export * from './.svg-sprite'`.
|
||||
|
||||
## SpriteViewer
|
||||
|
||||
The Viewer is interactive and imported from a client-only entry. Create a separate Client Component page or child component:
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
|
||||
import { SpriteViewer } from '@gromlab/svg-sprites/react'
|
||||
|
||||
const sources = [
|
||||
() => import('@/ui/file-manager/svg-sprite/manifest'),
|
||||
() => import('@/ui/navigation/svg-sprite/manifest'),
|
||||
]
|
||||
|
||||
export default function SpritesPage() {
|
||||
return <SpriteViewer sources={sources} title="Project icons" />
|
||||
}
|
||||
```
|
||||
|
||||
The `import()` paths must be string literals. Keep production icon imports outside `@gromlab/svg-sprites/react`: the package React entry is only for the Viewer and contains `'use client'`.
|
||||
|
||||
## Verification
|
||||
|
||||
Start with generation and typechecking:
|
||||
|
||||
```bash
|
||||
npm run sprite:file-manager
|
||||
npm run typecheck
|
||||
```
|
||||
|
||||
If the target or Next build/deployment pipeline changed, or a runtime issue is being diagnosed, run the project's production build configured for the selected bundler:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
After the required generation and typecheck, verify that:
|
||||
|
||||
- `.svg-sprite/svg-sprite.manifest.js` contains the exact target `next@app/turbopack` or `next@app/webpack`;
|
||||
- the server page compiles without adding `'use client'`.
|
||||
|
||||
During a conditional production/runtime check, also verify that:
|
||||
|
||||
- HTML contains an `href` to `.svg#id`, and the asset URL is available after `next start`;
|
||||
- the URL does not use `data:`, `file:`, or `blob:`;
|
||||
- SSR markup and the hydrated page refer to the same asset;
|
||||
- complex SVGs were examined according to [complex-svg.md](complex-svg.md); claim visual and accessibility results only when the corresponding tools are available.
|
||||
|
||||
## Common failures
|
||||
|
||||
- `Next.js mode requires a router and bundler`: `--mode next` is invalid; specify the full mode.
|
||||
- Build passes with one bundler but the runtime asset breaks with another: regenerate for the target actually used by the build.
|
||||
- Webpack was selected, but the build used Turbopack: use the project's Webpack build command and `next@app/webpack`.
|
||||
- Viewer causes a Server Component error: the Viewer file needs `'use client'`; the generated icon component does not.
|
||||
- Config not found: pass the full path to an existing `.ts`, `.js`, or `.json` config file.
|
||||
- Two CI jobs generate different targets in one checkout: separate their directories or ensure each job uses one consistent target.
|
||||
- Asset is missing under `basePath`/CDN: inspect Next asset handling and deployment configuration; do not replace the generated URL manually.
|
||||
- Package subpath type error: use TypeScript 5+ with `moduleResolution: "bundler"`, `"node16"`, or `"nodenext"`.
|
||||
|
||||
To invoke the generator from a build script, open [programmatic-api.md](programmatic-api.md).
|
||||
@@ -1,138 +0,0 @@
|
||||
# Next.js Pages Router: operational reference
|
||||
|
||||
## When to use this reference
|
||||
|
||||
Use this document for pages under `pages/` or `src/pages/`, including SSR with `getServerSideProps`, SSG, and client-side navigation. If the route is under `app/`, use [next-app.md](next-app.md).
|
||||
|
||||
## Selecting a target
|
||||
|
||||
| Bundler | CLI mode |
|
||||
|---|---|
|
||||
| Turbopack | `next@pages/turbopack` |
|
||||
| Webpack 5 | `next@pages/webpack` |
|
||||
|
||||
Determine the actual script flags before selecting a mode. The presence of the Pages Router does not automatically imply Webpack; select the target from the bundler used by the project.
|
||||
|
||||
## Structure and config
|
||||
|
||||
```text
|
||||
src/ui/file-manager/svg-sprite/
|
||||
├── icons/
|
||||
│ ├── check.svg
|
||||
│ └── folder.svg
|
||||
└── svg-sprite.config.ts
|
||||
```
|
||||
|
||||
Install the package as a development dependency:
|
||||
|
||||
```bash
|
||||
npm install --save-dev @gromlab/svg-sprites
|
||||
```
|
||||
|
||||
```ts
|
||||
import { defineSpriteConfig } from '@gromlab/svg-sprites'
|
||||
|
||||
export default defineSpriteConfig({
|
||||
mode: 'next@pages/webpack',
|
||||
name: 'file-manager',
|
||||
description: 'File manager icons',
|
||||
inputFolder: './icons',
|
||||
inputFiles: ['../../../../shared/icons/check.svg'],
|
||||
})
|
||||
```
|
||||
|
||||
The project chooses the directory containing each specific sprite config, and it does not have to be a module/feature directory; each config describes one of potentially many application sprites.
|
||||
|
||||
All source paths are resolved from the config directory. `inputFolder` defaults to `./icons`, and scanning is shallow. `inputFiles` is merged with the folder. The same path is deduplicated, but two different `check.svg` files conflict. An explicitly configured missing folder is an error even when `inputFiles` is non-empty.
|
||||
|
||||
`name` uses kebab-case and determines the public names. `FileManagerIcon` below is only an example generated name for `name: 'file-manager'`. Next modes always create `stack`, not `symbol`.
|
||||
|
||||
## Commands
|
||||
|
||||
Example lifecycle scripts for Webpack:
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts",
|
||||
"predev": "npm run sprite:file-manager",
|
||||
"prebuild": "npm run sprite:file-manager",
|
||||
"pretypecheck": "npm run sprite:file-manager"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If pre-scripts already exist, add generation to their current command chain. Generated imports are absent from Git, so generation must precede TypeScript and Next compilation. For Turbopack, replace the complete mode with `next@pages/turbopack`. Run `npm run sprite:file-manager` for the first generation.
|
||||
|
||||
## Usage in the Pages Router
|
||||
|
||||
```tsx
|
||||
import { FileManagerIcon } from '@/ui/file-manager/svg-sprite'
|
||||
|
||||
export default function FilesPage() {
|
||||
return <FileManagerIcon icon="folder" className="icon" aria-label="Folder" />
|
||||
}
|
||||
|
||||
export function getServerSideProps() {
|
||||
return { props: {} }
|
||||
}
|
||||
```
|
||||
|
||||
`width` and `height` are optional in JSX; set the size with a CSS class or `wrapped`. The component works during SSR, SSG, and client navigation. It uses `new URL('./sprite.svg', import.meta.url).href` so Next emits an external hashed asset. Do not copy the generated SVG into `public` or construct the URL manually.
|
||||
|
||||
Import the component and types from the local user-owned `svg-sprite/index.ts`, which re-exports `.svg-sprite`. Do not edit `.svg-sprite` or the generated `.gitignore`.
|
||||
|
||||
## SpriteViewer
|
||||
|
||||
Pages Router components run on the client as well, so a separate `'use client'` directive is unnecessary:
|
||||
|
||||
```tsx
|
||||
import { SpriteViewer } from '@gromlab/svg-sprites/react'
|
||||
|
||||
const sources = [
|
||||
() => import('@/ui/file-manager/svg-sprite/manifest'),
|
||||
() => import('@/ui/navigation/svg-sprite/manifest'),
|
||||
]
|
||||
|
||||
export default function SpritesPage() {
|
||||
return <SpriteViewer sources={sources} title="Project icons" />
|
||||
}
|
||||
```
|
||||
|
||||
Use literal imports and expose the page only as a debug/internal tool. If the page participates in production routing, restrict access through the application's own controls.
|
||||
|
||||
## Verification
|
||||
|
||||
```bash
|
||||
npm run sprite:file-manager
|
||||
npm run typecheck
|
||||
```
|
||||
|
||||
If the target or Next build/deployment pipeline changed, or a runtime issue is being diagnosed, run the project's production build configured for the selected bundler:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
After the required generation and typecheck, verify that:
|
||||
|
||||
- `.svg-sprite/svg-sprite.manifest.js` contains `next@pages/turbopack` or `next@pages/webpack`;
|
||||
- `getServerSideProps`/`getStaticProps` does not import the package React Viewer entry.
|
||||
|
||||
After the conditional production build, and only when browser tools are available, inspect the SSR route and navigation to it:
|
||||
|
||||
- initial HTML contains `.svg#id`;
|
||||
- the SVG URL responds successfully and is not `data:`, `file:`, or `blob:`;
|
||||
- after client navigation, the icon keeps the same valid URL;
|
||||
- gradients, masks, and colors were examined according to [complex-svg.md](complex-svg.md); do not claim a visual or accessibility result without the necessary tools.
|
||||
|
||||
## Common failures
|
||||
|
||||
- Mode uses `next@app/...`: the module may generate, but its manifest and target contract are wrong; use `next@pages/...`.
|
||||
- The command and mode select different bundlers: use the project's matching build command and regenerate.
|
||||
- Viewer manifest is missing from the chunk: the `import()` path must be a string literal and must exist before the Next build.
|
||||
- Icon appears after a full reload but disappears during navigation: check external asset availability with `basePath`, `assetPrefix`, and the production origin.
|
||||
- `Refusing to overwrite a user file`: the sprite directory contains an unmanaged file with a reserved name; move it.
|
||||
- Icon name is absent from the type: folder scanning is not recursive; move the SVG to the top level or add its exact path to `inputFiles`.
|
||||
|
||||
For programmatic generation, use [programmatic-api.md](programmatic-api.md).
|
||||
@@ -18,23 +18,28 @@ await generateSprite('src/ui/icons/svg-sprite.config.ts')
|
||||
await generateSprite('src/ui/icons', {
|
||||
mode: 'react@vite',
|
||||
name: 'app',
|
||||
inputFolder: './icons',
|
||||
input: './icons',
|
||||
})
|
||||
```
|
||||
|
||||
A directory enables config-less mode. `mode` must exist after settings are merged.
|
||||
|
||||
`input?: string | string[]` defaults to `./icons`. Each value is a folder, an exact SVG file, or a glob, resolved from the config directory or the config-less source directory. Folder scans are shallow; recurse only with an explicit glob such as `./icons/**/*.svg`. Arrays combine sources, and items prefixed with `!` exclude matches. Every positive item must resolve to at least one SVG. The final files are deduplicated and sorted, while different files with the same basename cause an error.
|
||||
|
||||
## Overrides
|
||||
|
||||
```ts
|
||||
await generateSprite('src/ui/icons/custom.json', {
|
||||
mode: 'react@webpack',
|
||||
inputFiles: ['../../shared/search.svg'],
|
||||
input: [
|
||||
'../../shared/icons/**/*.svg',
|
||||
'!../../shared/icons/legacy-*.svg',
|
||||
],
|
||||
transform: { addTransition: false },
|
||||
})
|
||||
```
|
||||
|
||||
Values are applied as `defaults → config → API overrides`. `transform` is merged by field; a supplied `inputFiles` replaces the config array.
|
||||
Values are applied as `defaults → config → API overrides`. `transform` is merged by field; a supplied `input` replaces the config value.
|
||||
|
||||
The specialized `generateReactSprite` and `generateNextSprite` functions remain as compatibility wrappers, but prefer `generateSprite` in new code.
|
||||
|
||||
|
||||
@@ -1,166 +0,0 @@
|
||||
# React with Vite: operational reference
|
||||
|
||||
## When to use this reference
|
||||
|
||||
Use this document when the project uses React without Next.js and is built with Vite, or when the generated component contains a `sprite.svg?no-inline` import. Do not apply this target to Webpack; use [react-webpack.md](react-webpack.md) instead.
|
||||
|
||||
## Establish context first
|
||||
|
||||
1. Check `package.json`: confirm React, Vite, and the actual `dev`, `build`, and `typecheck` commands.
|
||||
2. Find existing `svg-sprite.config.ts` files and scripts containing `svg-sprites`. Do not create a second directory for an existing sprite.
|
||||
3. Choose a target directory for the specific sprite. It does not have to be a module or feature directory; pass the full path to its config file.
|
||||
4. Do not manually edit `.svg-sprite` or the local `.gitignore`; the generator owns them. The root `index.ts` is application-owned.
|
||||
|
||||
Minimal structure:
|
||||
|
||||
```text
|
||||
src/ui/file-manager/svg-sprite/
|
||||
├── icons/
|
||||
│ ├── check.svg
|
||||
│ └── folder.svg
|
||||
└── svg-sprite.config.ts
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
Install the package as a development dependency:
|
||||
|
||||
```bash
|
||||
npm install --save-dev @gromlab/svg-sprites
|
||||
```
|
||||
|
||||
`src/ui/file-manager/svg-sprite/svg-sprite.config.ts`:
|
||||
|
||||
```ts
|
||||
import { defineSpriteConfig } from '@gromlab/svg-sprites'
|
||||
|
||||
export default defineSpriteConfig({
|
||||
mode: 'react@vite',
|
||||
name: 'file-manager',
|
||||
description: 'File manager icons',
|
||||
inputFolder: './icons',
|
||||
inputFiles: ['../../../../shared/icons/check.svg'],
|
||||
})
|
||||
```
|
||||
|
||||
- Each config describes one specific sprite; an application may contain many independent configs and sprites.
|
||||
- Every config path is resolved relative to the directory containing `svg-sprite.config.ts`.
|
||||
- `inputFolder` defaults to `./icons`; folder scanning is shallow and includes files ending in `.svg`.
|
||||
- `inputFolder` and `inputFiles` are merged, and duplicate absolute paths are deduplicated.
|
||||
- If `inputFiles` is non-empty and the implicit `./icons` directory is absent, generation uses only the list. An explicitly configured missing `inputFolder` is always an error.
|
||||
- Different files with the same basename, such as two `check.svg` files, conflict because they define the same public icon name.
|
||||
- `name` must use kebab-case and begin with an ASCII letter. `FileManagerIcon`, `FileManagerIconName`, and `fileManagerIconNames` below are only examples of generated names for `name: 'file-manager'`.
|
||||
- The React preset always creates the `stack` format; `symbol` cannot be selected here.
|
||||
|
||||
## Command and scripts
|
||||
|
||||
Add the local CLI to `package.json` and run it before processes that need generated imports:
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts",
|
||||
"predev": "npm run sprite:file-manager",
|
||||
"prebuild": "npm run sprite:file-manager",
|
||||
"pretypecheck": "npm run sprite:file-manager"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If the project already has `predev` or `prebuild`, integrate generation into the existing orchestration instead of overwriting the script.
|
||||
|
||||
Run `npm run sprite:file-manager` for the first generation.
|
||||
|
||||
## Usage
|
||||
|
||||
Import only from the public local entry point:
|
||||
|
||||
```tsx
|
||||
import { FileManagerIcon, fileManagerIconNames } from './svg-sprite'
|
||||
import type { FileManagerIconName, FileManagerIconStyle } from './svg-sprite'
|
||||
|
||||
const colorStyle: FileManagerIconStyle = {
|
||||
'--icon-color-1': '#2563eb',
|
||||
}
|
||||
|
||||
export function FolderIcon({ icon }: { icon: FileManagerIconName }) {
|
||||
return <FileManagerIcon icon={icon} className="icon" style={colorStyle} />
|
||||
}
|
||||
|
||||
export const availableIcons = fileManagerIconNames
|
||||
```
|
||||
|
||||
`width` and `height` are optional in JSX; a CSS class can control the size. Without `wrapped`, the component renders `<svg>` and accepts SVG attributes. With `wrapped={true}`, the root becomes a `<span>` and the inner SVG fills its width and height:
|
||||
|
||||
```tsx
|
||||
<FileManagerIcon icon="folder" wrapped className="iconBox" />
|
||||
```
|
||||
|
||||
For normal imports, use a user-owned barrel with `export * from './.svg-sprite'`; the remaining files in the hidden directory are internal.
|
||||
|
||||
## Target-specific behavior
|
||||
|
||||
The Vite target generates a static import:
|
||||
|
||||
```ts
|
||||
import spriteUrl from './sprite.svg?no-inline'
|
||||
```
|
||||
|
||||
The `?no-inline` query is required: it prevents Vite from converting a small SVG into a data URL. Do not remove the query or copy the generated SVG into `public`; Vite must emit a separate content-hashed asset.
|
||||
|
||||
Use the same mechanism for low-level `<use>` integration:
|
||||
|
||||
```tsx
|
||||
import spriteUrl from './svg-sprite/.svg-sprite/sprite.svg?no-inline'
|
||||
|
||||
<svg className="icon">
|
||||
<use href={`${spriteUrl}#check`} />
|
||||
</svg>
|
||||
```
|
||||
|
||||
A manual `#check` fragment is safe only for names matching `^[a-zA-Z][a-zA-Z0-9_-]*$`. For spaces and other characters, the generated component uses a stable hash ID; the exact ID is available in `.svg-sprite/svg-sprite.manifest.js`.
|
||||
|
||||
## SpriteViewer
|
||||
|
||||
After generation, add the Viewer only to a debug route:
|
||||
|
||||
```tsx
|
||||
import { SpriteViewer } from '@gromlab/svg-sprites/react'
|
||||
import type { SpriteManifestModule } from '@gromlab/svg-sprites/react'
|
||||
|
||||
const sources = import.meta.glob<SpriteManifestModule>(
|
||||
'/src/**/svg-sprite/.svg-sprite/svg-sprite.manifest.js',
|
||||
)
|
||||
|
||||
export function IconsDebugPage() {
|
||||
return <SpriteViewer sources={sources} title="Project icons" />
|
||||
}
|
||||
```
|
||||
|
||||
The `import.meta.glob` argument must remain a string literal. Generation must finish before Vite starts, or new manifests will not be included in the glob.
|
||||
|
||||
## Verification
|
||||
|
||||
```bash
|
||||
npm run sprite:file-manager
|
||||
npm run typecheck
|
||||
```
|
||||
|
||||
These are the required quick checks. Inspect the generated files statically:
|
||||
|
||||
- the user-owned `index.ts` re-exports the component, props, style, name union, and runtime array from `.svg-sprite`;
|
||||
- `.svg-sprite/svg-sprite.manifest.js` contains `target: "vite"`, `format: "stack"`, and the expected icon count.
|
||||
|
||||
If the target or asset pipeline changed, or a runtime issue is being diagnosed, also run the production build. When browser tools are available, inspect Network for a separate `.svg` asset rather than `data:image/svg+xml`, a successful URL, and `<use href="...svg#...">`. For complex colors, `defs`, and dimensions, follow [complex-svg.md](complex-svg.md); do not claim a visual or accessibility result without the necessary tools.
|
||||
|
||||
## Common failures
|
||||
|
||||
- Config not found: pass the full path to an existing `.ts`, `.js`, or `.json` config file.
|
||||
- `Sprite mode is required`: set `mode: 'react@vite'` in the config or pass `--mode react@vite`.
|
||||
- Icon missing from autocomplete: check the case-sensitive `.svg` suffix and shallow location, then regenerate before typechecking.
|
||||
- `Refusing to overwrite a user file`: do not remove the marker or bypass the writer; move the user-owned file or choose another sprite directory.
|
||||
- Viewer is empty: check the literal glob, the existence of `.svg-sprite/svg-sprite.manifest.js`, and the `predev` execution order.
|
||||
- SVG was inlined: confirm that the module targets `vite` and its import still contains `?no-inline`.
|
||||
- TypeScript cannot resolve the package subpath: use TypeScript 5+ with `moduleResolution: "bundler"`, `"node16"`, or `"nodenext"`.
|
||||
|
||||
For execution without the CLI, use [programmatic-api.md](programmatic-api.md).
|
||||
@@ -1,152 +0,0 @@
|
||||
# React with Webpack 5: operational reference
|
||||
|
||||
## When to use this reference
|
||||
|
||||
Use this document for a React application built with Webpack 5 when SVGs must be processed through Asset Modules. If the project uses Vite, see [react-vite.md](react-vite.md). For Next.js, do not select `react@webpack`; router-specific targets are documented in [next-app.md](next-app.md) and [next-pages.md](next-pages.md).
|
||||
|
||||
## Diagnose the environment before making changes
|
||||
|
||||
1. Confirm Webpack major version 5 in `package.json` or the lockfile.
|
||||
2. Inspect `.svg` entries in `module.rules`, `output.publicPath`, the dev server, and existing asset conventions.
|
||||
3. Find existing `svg-sprite.config.ts` files and generation scripts.
|
||||
4. Choose the project directory for the specific sprite. It does not have to match a module or feature directory. Pass the full path to its config file.
|
||||
|
||||
Minimal structure:
|
||||
|
||||
```text
|
||||
src/ui/file-manager/svg-sprite/
|
||||
├── icons/
|
||||
│ ├── check.svg
|
||||
│ └── folder.svg
|
||||
└── svg-sprite.config.ts
|
||||
```
|
||||
|
||||
## Configuration and generation
|
||||
|
||||
Install the package as a development dependency:
|
||||
|
||||
```bash
|
||||
npm install --save-dev @gromlab/svg-sprites
|
||||
```
|
||||
|
||||
```ts
|
||||
import { defineSpriteConfig } from '@gromlab/svg-sprites'
|
||||
|
||||
export default defineSpriteConfig({
|
||||
mode: 'react@webpack',
|
||||
name: 'file-manager',
|
||||
description: 'File manager icons',
|
||||
inputFolder: './icons',
|
||||
inputFiles: ['../../../../shared/icons/check.svg'],
|
||||
})
|
||||
```
|
||||
|
||||
Each config describes one of potentially many independent application sprites.
|
||||
|
||||
Paths are relative to `svg-sprite.config.ts`. The folder is scanned only at its top level. `inputFolder` and `inputFiles` are merged; duplicate paths are deduplicated, but equal basenames from different files cause an ID conflict. The implicit `./icons` folder may be absent when `inputFiles` is non-empty; an explicitly configured missing folder is an error. `FileManagerIcon` below is only an example generated name for `name: 'file-manager'`.
|
||||
|
||||
Recommended lifecycle hooks:
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts",
|
||||
"predev": "npm run sprite:file-manager",
|
||||
"prebuild": "npm run sprite:file-manager",
|
||||
"pretypecheck": "npm run sprite:file-manager"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Do not overwrite existing pre-scripts; add generation to their current command chain. Run `npm run sprite:file-manager` for the first generation. The React preset always emits `stack`.
|
||||
|
||||
## Public component
|
||||
|
||||
```tsx
|
||||
import { FileManagerIcon } from './svg-sprite'
|
||||
|
||||
export function OpenFolderButton() {
|
||||
return (
|
||||
<button type="button">
|
||||
<FileManagerIcon icon="folder" className="icon" aria-hidden />
|
||||
Open
|
||||
</button>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
`width` and `height` are optional in JSX; set the size with a CSS class or `wrapped`. The user-owned barrel also exports `FileManagerIconProps`, `FileManagerIconStyle`, `FileManagerIconName`, and `fileManagerIconNames`. Do not edit `.svg-sprite` or the generated `.gitignore`.
|
||||
|
||||
## Webpack target behavior
|
||||
|
||||
The generated component obtains its URL only through this static expression:
|
||||
|
||||
```ts
|
||||
const spriteUrl = new URL('./sprite.svg', import.meta.url).href
|
||||
```
|
||||
|
||||
Webpack 5 recognizes it as an Asset Module and replaces it with the public URL of a separate SVG. For correct handling:
|
||||
|
||||
- do not move the path into a variable or modify the generated expression;
|
||||
- ensure Babel/TypeScript does not transform `import.meta.url` before Webpack processes it;
|
||||
- do not let `@svgr/webpack`, `svg-inline-loader`, `raw-loader`, or a general SVG rule intercept `svg-sprite/.svg-sprite/sprite.svg`;
|
||||
- with custom rules, either exclude the generated sprite from component/raw loaders or add a dedicated `type: 'asset/resource'` rule;
|
||||
- check `output.publicPath`, especially for CDN, subpath, and dev-server deployments.
|
||||
|
||||
Example dedicated rule when an existing loader intercepts every SVG:
|
||||
|
||||
```js
|
||||
{
|
||||
test: /svg-sprite[\\/]\.svg-sprite[\\/]sprite\.svg$/,
|
||||
type: 'asset/resource',
|
||||
}
|
||||
```
|
||||
|
||||
Integrate this rule with the project's current configuration; do not add a duplicate matcher when standard Asset Modules already handle `new URL` correctly.
|
||||
|
||||
## SpriteViewer
|
||||
|
||||
Webpack does not provide `import.meta.glob`. Pass static lazy imports with string-literal paths:
|
||||
|
||||
```tsx
|
||||
import { SpriteViewer } from '@gromlab/svg-sprites/react'
|
||||
|
||||
const sources = [
|
||||
() => import('./ui/file-manager/svg-sprite/manifest'),
|
||||
() => import('./ui/navigation/svg-sprite/manifest'),
|
||||
]
|
||||
|
||||
export function IconsDebugPage() {
|
||||
return <SpriteViewer sources={sources} title="Project icons" />
|
||||
}
|
||||
```
|
||||
|
||||
A dynamically assembled `import(path)` is not suitable: Webpack cannot reliably associate the manifests with their assets. Expose the Viewer only on a debug/internal route.
|
||||
|
||||
## Verification
|
||||
|
||||
```bash
|
||||
npm run sprite:file-manager
|
||||
npm run typecheck
|
||||
```
|
||||
|
||||
These are the required quick checks. After generation, verify that:
|
||||
|
||||
- `.svg-sprite/svg-sprite.manifest.js` contains `target: "webpack"` and `format: "stack"`;
|
||||
- the generated component contains `new URL('./sprite.svg', import.meta.url).href`.
|
||||
|
||||
A production build and browser/Network checks are additionally required when the target, Webpack asset rules, `publicPath`/deployment pipeline changed, or a runtime issue is being diagnosed. In that case, verify a separate hashed SVG asset, an HTTP(S) `.svg#id` URL, content type, fragment ID, and the correct CDN/subpath URL. Do not claim visual or accessibility correctness without the necessary tools.
|
||||
|
||||
For icons with gradients, filters, masks, or internal CSS, perform the checks in [complex-svg.md](complex-svg.md).
|
||||
|
||||
## Common failures
|
||||
|
||||
- `Unsupported React target`: the programmatic API received a value other than `'webpack'`; the CLI mode must be exactly `react@webpack`.
|
||||
- Webpack tries to render the SVG as a React component: the generated sprite matched the SVGR rule; exclude it or prioritize `asset/resource`.
|
||||
- URL points to the wrong host/subpath: fix `output.publicPath` and runtime deployment settings, not the generated file.
|
||||
- `import.meta` is unsupported: confirm the build really uses Webpack 5 and that an intermediate transpiler preserves the expression.
|
||||
- Viewer does not load the manifest: check the literal path, chunk loading, and generation before compilation.
|
||||
- `Refusing to overwrite a user file`: the directory already contains a user-owned `.gitignore` or file in `.svg-sprite`; move it rather than bypassing protection.
|
||||
- Icon color does not change: use `color` for monochrome icons or `--icon-color-N` through `<svg><use>`; page CSS does not cross into `<img>`.
|
||||
|
||||
For custom build orchestration, see [programmatic-api.md](programmatic-api.md).
|
||||
@@ -11,8 +11,9 @@
|
||||
|
||||
## Справочники по необходимости
|
||||
|
||||
- Для React + Vite открой [React + Vite](./references/react-vite.md), для React с собственным Webpack 5 — [React + Webpack](./references/react-webpack.md).
|
||||
- Для Next.js открой руководство для [App Router](./references/next-app.md) или [Pages Router](./references/next-pages.md) и выбери раздел фактического сборщика.
|
||||
- Для статической публикации открой [bare standalone](./references/guides/standalone.md). Для vanilla-приложений со сборщиком открой [standalone + Vite](./references/guides/standalone-vite.md) или [standalone + Webpack](./references/guides/standalone-webpack.md).
|
||||
- Для 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).
|
||||
- Для Next.js Pages Router открой exact guide для [Turbopack](./references/guides/next-pages-turbopack.md) или [Webpack](./references/guides/next-pages-webpack.md).
|
||||
- Для вызова генератора из Node.js открой [программный API](./references/programmatic-api.md).
|
||||
- Для gradients, filters, `url(#...)`, нестандартных цветов и проблем с `viewBox` открой [сложные SVG](./references/complex-svg.md).
|
||||
- Полную пользовательскую документацию используй как вторичный источник: [README пакета](./references/upstream/README_RU.md).
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
## Что делает пакет
|
||||
|
||||
`@gromlab/svg-sprites` — CLI-генератор SVG-спрайтов для пользовательских SVG-файлов. Пакет не содержит собственного набора иконок: он собирает SVG проекта во внешний sprite asset, поддерживает standalone-проекты и для React/Next.js создаёт типизированный компонент.
|
||||
`@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 не импортирует `@gromlab/svg-sprites` во время выполнения. Устанавливай пакет как development dependency, чтобы config helpers и локальный CLI использовали версию из lockfile проекта.
|
||||
Generated production runtime и declarations не импортируют `@gromlab/svg-sprites`. Генерация работает через `npx --package` с зафиксированной версией без добавления package в проект. Устанавливай его как development dependency только для Viewer, package-типов config или программного API.
|
||||
|
||||
@@ -21,10 +21,10 @@ CLI принимает ровно один путь. Путь к файлу `.ts
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"sprite:<name>": "svg-sprites <path-to-config>",
|
||||
"sprite:<name>:cli": "svg-sprites --mode <mode-key> <sprite-directory>"
|
||||
"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>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Не используй неполные `react`, `next@app`, `next@pages`, `standalone@` или удалённый `legacy`. Bare `standalone` выбирай только когда приложение само публикует SVG; для Vite/Webpack используй соответствующий полный key. Для нескольких спрайтов создай отдельную команду для каждого config-файла или каталога.
|
||||
Генерация через `npx` не добавляет package в проект. В CI укажи точную версию вместо `latest`. Не используй неполные `react`, `next@app`, `next@pages`, `standalone@` или удалённый `legacy`. Bare `standalone` выбирай только когда приложение само публикует SVG; для Vite/Webpack используй соответствующий полный key. Для нескольких спрайтов создай отдельную команду для каждого config-файла или каталога.
|
||||
|
||||
@@ -9,14 +9,14 @@
|
||||
5. Для нового спрайта выбери целевой каталог, не навязывая конкретный слой или архитектуру приложения.
|
||||
6. Проверь TypeScript и alias-настройки. Для package subpath exports нужен TypeScript 5+ с `moduleResolution: 'bundler'`, `'node16'` или `'nodenext'`.
|
||||
|
||||
Все пути конфига считаются относительно каталога, содержащего явно переданный config-файл; в config-less режиме — относительно переданного каталога:
|
||||
Все input-пути считаются относительно каталога, содержащего явно переданный config-файл; в config-less режиме — относительно переданного каталога. Проверяй `input` как единый контракт:
|
||||
|
||||
- `inputFolder` по умолчанию равен `./icons`;
|
||||
- `inputFiles` содержит дополнительные относительные пути к отдельным SVG и объединяется с `inputFolder`;
|
||||
- одинаковый абсолютный файл дедуплицируется, но разные файлы с одинаковым basename иконки вызывают ошибку;
|
||||
- сканирование `inputFolder` плоское: читаются только непосредственные файлы с окончанием `.svg`, вложенные каталоги не обходятся;
|
||||
- отсутствующая явно заданная `inputFolder` является ошибкой;
|
||||
- если `inputFolder` не задана, `./icons` отсутствует, а `inputFiles` непустой, генерация работает только по `inputFiles`;
|
||||
- пустой итоговый набор, отсутствующий файл или путь не к `.svg` является ошибкой.
|
||||
- `input?: string | string[]` по умолчанию равен `./icons`;
|
||||
- каждая строка задаёт папку, точный SVG-файл или glob;
|
||||
- папка сканируется плоско; вложенные файлы включаются только явным recursive glob, например `./icons/**/*.svg`;
|
||||
- массив объединяет positive-источники, а элемент с префиксом `!` исключает свои совпадения из общего набора;
|
||||
- каждый positive-источник должен разрешаться хотя бы в один SVG, поэтому отсутствующая или пустая папка, glob без совпадений, отсутствующий файл или точный путь не к SVG являются ошибкой;
|
||||
- разрешённые файлы дедуплицируются и детерминированно сортируются;
|
||||
- разные файлы с одинаковым basename конфликтуют, даже если получены из разных источников.
|
||||
|
||||
Не копируй общий SVG в несколько папок: добавь его относительный путь в `inputFiles` каждого нужного спрайта. Если требуется рекурсивная структура, перечисли файлы явно или измени структуру источников; генератор не выполняет recursive scan.
|
||||
Не копируй общий SVG в несколько папок: добавь его точный путь или подходящий glob в `input` каждого нужного спрайта. Используй `**/*.svg` только для намеренного рекурсивного включения.
|
||||
|
||||
@@ -12,27 +12,21 @@ src/ui/file-manager/svg-sprite/
|
||||
|
||||
Один `svg-sprite.config.ts` создаёт один независимый спрайт. Для нескольких наборов выбери несколько каталогов и дай каждому уникальное `name`.
|
||||
|
||||
Установи пакет как development dependency:
|
||||
|
||||
```bash
|
||||
npm install --save-dev @gromlab/svg-sprites
|
||||
```
|
||||
|
||||
Используй config helper для autocomplete и проверки типов:
|
||||
Генератор не нужно устанавливать в проект. Начни с plain config без package
|
||||
import:
|
||||
|
||||
```ts
|
||||
import { defineSpriteConfig } from '@gromlab/svg-sprites'
|
||||
|
||||
export default defineSpriteConfig({
|
||||
export default {
|
||||
mode: 'react@vite',
|
||||
name: 'file-manager',
|
||||
description: 'Иконки файлового менеджера',
|
||||
inputFolder: './icons',
|
||||
inputFiles: ['../../shared/icons/close.svg'],
|
||||
})
|
||||
input: ['./icons', '../../shared/icons/close.svg'],
|
||||
}
|
||||
```
|
||||
|
||||
Контракт объекта одинаков для React и Next.js; отличается полный `mode`.
|
||||
`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` не задан, генератор выводит его из каталога.
|
||||
|
||||
@@ -41,7 +35,7 @@ export default defineSpriteConfig({
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
@@ -49,9 +43,18 @@ export default defineSpriteConfig({
|
||||
|
||||
Для Next.js укажи в config полный ключ, например `next@app/turbopack`. Для нескольких спрайтов добавь по команде `sprite:<name>` на каждый config-файл и последовательно вызови их из `sprites`.
|
||||
|
||||
Generated-файлы в `.svg-sprite` по умолчанию исключаются из Git, поэтому запускай `sprites` до процессов, которым нужны компонент, типы или asset. Если проект импортирует корень sprite-модуля, создай пользовательский `index.ts` с `export * from './.svg-sprite'`. Добавь генерацию к `predev`, `prebuild` и, если есть `typecheck`, к `pretypecheck`.
|
||||
Чтобы задать источники через CLI, повторяй `--input <path-or-glob>`; значения образуют тот же массивный контракт, включая исключения с `!`:
|
||||
|
||||
Если lifecycle script отсутствует, создай его. Если он уже существует, сохрани его команду и допиши генерацию через `&&`, например преобразуй `"prebuild": "npm run lint"` в `"prebuild": "npm run lint && npm run sprites"`. Никогда не заменяй существующий `pre*` одной генерацией и не создавай второй одноимённый JSON key.
|
||||
```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.
|
||||
|
||||
Запусти первую генерацию вручную:
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ svg-sprite/
|
||||
├── index.ts # необязательный пользовательский barrel
|
||||
├── .gitignore # управляет генератор
|
||||
└── .svg-sprite/
|
||||
├── state.json # реестр владения и версия контракта
|
||||
├── index.js
|
||||
├── index.d.ts
|
||||
├── icon-data.js
|
||||
@@ -25,23 +24,25 @@ svg-sprite/
|
||||
|
||||
Standalone не создаёт `react/`. Bare `standalone` генерирует `sprite.svg` и
|
||||
`svg-sprite.manifest.json`; `standalone@vite`/`standalone@webpack` дополнительно
|
||||
генерируют `index.*`, `icon-data.*` и resolved manifest.
|
||||
генерируют `index.*`, `icon-data.*` и resolved manifest. Их `index.*` также
|
||||
содержит нативный generated Web Component; bare `standalone` не получает JS runtime,
|
||||
не создаёт и не изменяет `.gitignore`.
|
||||
|
||||
Редактируй исходные SVG, config-файл и пользовательский `index.ts`. Не изменяй вручную `.gitignore` и содержимое `.svg-sprite`: повторная генерация их перезапишет. Для импорта из корня sprite-модуля создай barrel:
|
||||
Редактируй исходные SVG, config-файл и пользовательский `index.ts`. Не изменяй вручную содержимое `.svg-sprite`: повторная генерация его перезапишет. Во всех modes, кроме bare `standalone`, generated `.gitignore` также находится под управлением генератора. Для импорта из корня sprite-модуля создай barrel:
|
||||
|
||||
```ts
|
||||
export * from './.svg-sprite'
|
||||
```
|
||||
|
||||
Генератор владеет `.gitignore` и файлами внутри `.svg-sprite`. Реестр `state.json` позволяет удалить устаревший generated-файл, но writer откажется перезаписывать или удалять файл без generated-маркера. Не удаляй маркер, не обходи отказ и не подменяй generated-пути symlink: перенеси пользовательский файл или выбери другой каталог.
|
||||
Генератор полностью владеет каталогом `.svg-sprite` и заменяет его при каждом запуске. Никогда не помещай туда пользовательские файлы. Генератор также владеет `.gitignore`, когда выбранный mode его создаёт; bare `standalone` оставляет существующий `.gitignore` без изменений. Generated-пути не должны содержать symlink.
|
||||
|
||||
Внутренний `index.js` экспортирует компонент из `react/react-component.js` и readonly-массив имён; соседний `index.d.ts` добавляет props/style-типы и union имени. Manifest содержит mode, URL, target, список и метаданные иконок для debug-инструментов и не импортируется production-компонентом.
|
||||
Внутренний `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 без React;
|
||||
- `standalone@webpack` использует Webpack Asset Modules без React;
|
||||
- `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; не добавляй клиентскую границу только ради иконки;
|
||||
|
||||
@@ -1,6 +1,20 @@
|
||||
## Использование, доступность и цвета
|
||||
|
||||
Имя компонента зависит от `name` конкретного спрайта. В примерах используется `name: 'file-manager'`, поэтому generated-компонент называется `FileManagerIcon`. Для `name: 'navigation'` используй сгенерированный `NavigationIcon`.
|
||||
Имя компонента зависит от `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-классом.
|
||||
|
||||
@@ -64,10 +78,10 @@ Generated-компонент не выбирает семантику за пр
|
||||
|
||||
Автозамена рассчитана на `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/react` только на debug-маршруте:
|
||||
`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('.../manifest')`;
|
||||
- в Webpack передай массив статических `() => import('.../.svg-sprite/svg-sprite.manifest.js')`;
|
||||
- в Next.js используй такие же статические loaders, а для App Router помести Viewer в отдельный файл с `'use client'`.
|
||||
|
||||
Viewer принимает manifests/loaders, показывает поиск, темы, цвета и примеры, но production-компоненты от него не зависят. Импортируй его из пакета, уже установленного как development dependency.
|
||||
Viewer принимает manifests/loaders, показывает поиск, темы, цвета и примеры, но production-компоненты от него не зависят.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
После изменения конфига или 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, а также `state.json`.
|
||||
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.
|
||||
|
||||
@@ -8,9 +8,10 @@
|
||||
| `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-файла. |
|
||||
| `Input directory does not exist` | Ошибка относительного пути или отсутствует явно заданная папка | Считай путь от каталога конфига; создай папку или исправь `inputFolder`. |
|
||||
| Иконки из подпапки не появились | Ожидался recursive scan | Перемести SVG на верхний уровень `inputFolder` или перечисли его в `inputFiles`. |
|
||||
| `SVG file does not exist`, `File is not an SVG` или пустой набор | Неверный `inputFiles`, расширение или источники | Исправь путь/расширение и обеспечь хотя бы один входной SVG. |
|
||||
| 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. |
|
||||
|
||||
@@ -131,7 +131,7 @@ transform: {
|
||||
|
||||
Не создавай вручную `#folder open`. Используй generated component либо `.svg-sprite/svg-sprite.manifest.js`, где записаны `name` и фактический `id`.
|
||||
|
||||
Разные файлы с одинаковым basename запрещены даже из разных directories. Переименуй один source осмысленно; порядок `inputFiles` не является способом выбрать победителя.
|
||||
Разные файлы с одинаковым basename запрещены даже из разных directories. Переименуй один source осмысленно; порядок или пересечение источников не выбирают победителя.
|
||||
|
||||
## Способ отображения
|
||||
|
||||
@@ -173,4 +173,4 @@ External stack fragment support и поведение paint servers могут
|
||||
- Ручной fragment не работает для имени с пробелом: используй ID из manifest.
|
||||
- Один сложный icon требует иных transforms: вынеси его в отдельный sprite; per-icon transform config отсутствует.
|
||||
|
||||
Target-specific запуск и проверка описаны в [react-vite.md](react-vite.md), [react-webpack.md](react-webpack.md), [next-app.md](next-app.md) и [next-pages.md](next-pages.md).
|
||||
Target-specific запуск и проверка описаны в exact-mode файлах каталога [guides](guides/standalone.md).
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
# Next.js App Router: операционный reference
|
||||
|
||||
## Когда открывать
|
||||
|
||||
Открывай этот документ, если приложение использует каталог `app/` или `src/app/` и нужен generated-компонент для Server Components, SSR или SSG. Сначала установи реальный сборщик: target для Turbopack и Webpack различается. Для `pages/` используй [next-pages.md](next-pages.md).
|
||||
|
||||
## Матрица target
|
||||
|
||||
| Сборщик | CLI mode |
|
||||
|---|---|
|
||||
| Turbopack | `next@app/turbopack` |
|
||||
| Webpack 5 | `next@app/webpack` |
|
||||
|
||||
Не выводи target только из наличия `next.config.*`. Проверь фактические flags в `dev`/`build`. Mode генератора и сборщик контрольной сборки должны совпадать.
|
||||
|
||||
## Подготовка каталога спрайта
|
||||
|
||||
```text
|
||||
src/ui/file-manager/svg-sprite/
|
||||
├── icons/
|
||||
│ ├── check.svg
|
||||
│ └── folder.svg
|
||||
└── svg-sprite.config.ts
|
||||
```
|
||||
|
||||
Установи пакет как development dependency:
|
||||
|
||||
```bash
|
||||
npm install --save-dev @gromlab/svg-sprites
|
||||
```
|
||||
|
||||
```ts
|
||||
import { defineSpriteConfig } from '@gromlab/svg-sprites'
|
||||
|
||||
export default defineSpriteConfig({
|
||||
mode: 'next@app/turbopack',
|
||||
name: 'file-manager',
|
||||
description: 'Иконки файлового менеджера',
|
||||
inputFolder: './icons',
|
||||
inputFiles: ['../../../../shared/icons/check.svg'],
|
||||
})
|
||||
```
|
||||
|
||||
- Каталог выбирает проект конкретного спрайта и не обязан быть module/feature-каталогом. Каждый config описывает один из потенциально многих спрайтов приложения.
|
||||
- `svg-sprite.config.ts` — рекомендуемое имя; CLI принимает явно переданный `.ts`, `.js` или `.json` файл с любым именем.
|
||||
- Пути считаются от его каталога; `inputFolder` сканируется нерекурсивно.
|
||||
- Локальная папка и `inputFiles` объединяются. Одинаковые basename разных файлов запрещены.
|
||||
- Если имя не задано, оно выводится из каталога: для папки `svg-sprite` берётся имя родительской папки. Явное `name` надёжнее при перемещениях.
|
||||
- `name` должен быть kebab-case и начинаться с буквы.
|
||||
- `FileManagerIcon` ниже — только пример generated-имени из `name: 'file-manager'`.
|
||||
- Next preset всегда генерирует `stack` и включает root `viewBox` спрайта; это не пользовательская настройка.
|
||||
|
||||
## Генерация
|
||||
|
||||
Пример scripts для Turbopack:
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts",
|
||||
"predev": "npm run sprite:file-manager",
|
||||
"prebuild": "npm run sprite:file-manager",
|
||||
"pretypecheck": "npm run sprite:file-manager"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Для Webpack замени только mode целиком на `next@app/webpack`. Не запускай два target последовательно для одного каталога: второй перезапишет generated target первого. Для первой генерации запусти `npm run sprite:file-manager`.
|
||||
|
||||
## Server Component
|
||||
|
||||
Generated production component не содержит `'use client'`. Импортируй его непосредственно в server `page.tsx` или `layout.tsx`:
|
||||
|
||||
```tsx
|
||||
import { FileManagerIcon } from '@/ui/file-manager/svg-sprite'
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<main>
|
||||
<FileManagerIcon icon="folder" className="icon" aria-label="Папка" />
|
||||
</main>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
`width` и `height` в JSX необязательны: размер можно задать CSS-классом или через `wrapped`. Не добавляй Client Component boundary только ради иконки. Generated module формирует asset URL через статический `new URL('./sprite.svg', import.meta.url).href`; один механизм используется при SSR и в браузере.
|
||||
|
||||
Не перемещай SVG в `public`. Генератор перезаписывает `.svg-sprite` и `.gitignore`; корневой `index.ts` принадлежит приложению и может содержать `export * from './.svg-sprite'`.
|
||||
|
||||
## SpriteViewer
|
||||
|
||||
Viewer интерактивен и импортируется из client-only entry. Создай отдельную Client Component страницу или дочерний компонент:
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
|
||||
import { SpriteViewer } from '@gromlab/svg-sprites/react'
|
||||
|
||||
const sources = [
|
||||
() => import('@/ui/file-manager/svg-sprite/manifest'),
|
||||
() => import('@/ui/navigation/svg-sprite/manifest'),
|
||||
]
|
||||
|
||||
export default function SpritesPage() {
|
||||
return <SpriteViewer sources={sources} title="Иконки проекта" />
|
||||
}
|
||||
```
|
||||
|
||||
Пути `import()` должны быть строковыми литералами. Оставь production icon imports за пределами `@gromlab/svg-sprites/react`: package React entry предназначен только для Viewer и содержит `'use client'`.
|
||||
|
||||
## Проверка
|
||||
|
||||
Сначала генерация и типы:
|
||||
|
||||
```bash
|
||||
npm run sprite:file-manager
|
||||
npm run typecheck
|
||||
```
|
||||
|
||||
Если менялись target или Next build/deployment pipeline либо диагностируется runtime, запусти production-сборку проекта, настроенную на выбранный сборщик:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
После обязательных генерации и typecheck проверь:
|
||||
|
||||
- `.svg-sprite/svg-sprite.manifest.js` содержит точный target `next@app/turbopack` либо `next@app/webpack`;
|
||||
- server page компилируется без добавления `'use client'`;
|
||||
|
||||
При условной production/runtime-проверке проверь также:
|
||||
|
||||
- HTML содержит `href` на `.svg#id`, а URL asset доступен после `next start`;
|
||||
- URL не использует `data:`, `file:` или `blob:`;
|
||||
- SSR markup и hydrated page ссылаются на один asset;
|
||||
- сложные SVG исследованы по [complex-svg.md](complex-svg.md); визуальный и a11y результат утверждай только при наличии соответствующих инструментов.
|
||||
|
||||
## Типовые ошибки
|
||||
|
||||
- `Next.js mode requires a router and bundler`: нельзя использовать `--mode next`; укажи полный mode.
|
||||
- Сборка проходит на одном bundler, а runtime asset ломается на другом: перегенерируй тем target, которым реально выполняется build.
|
||||
- Выбран Webpack, но build ушёл в Turbopack: используй Webpack-команду проекта и `next@app/webpack`.
|
||||
- Viewer вызывает ошибку Server Component: файл с Viewer должен иметь `'use client'`; generated icon component этого не требует.
|
||||
- Config не найден: передай полный путь к существующему `.ts`, `.js` или `.json` config-файлу.
|
||||
- Две CI jobs генерируют разные target в одном checkout: раздели каталоги или обеспечь один согласованный target на job.
|
||||
- Asset не найден под `basePath`/CDN: проверяй Next asset handling и deployment config, не подменяй generated URL вручную.
|
||||
- Ошибка package subpath types: используй TypeScript 5+ и `moduleResolution: "bundler"`, `"node16"` или `"nodenext"`.
|
||||
|
||||
Для вызова генератора из build script открой [programmatic-api.md](programmatic-api.md).
|
||||
@@ -1,138 +0,0 @@
|
||||
# Next.js Pages Router: операционный reference
|
||||
|
||||
## Когда открывать
|
||||
|
||||
Открывай этот документ для страниц в `pages/` или `src/pages/`, включая SSR через `getServerSideProps`, SSG и клиентские переходы. Если маршрут находится в `app/`, используй [next-app.md](next-app.md).
|
||||
|
||||
## Выбор target
|
||||
|
||||
| Сборщик | CLI mode |
|
||||
|---|---|
|
||||
| Turbopack | `next@pages/turbopack` |
|
||||
| Webpack 5 | `next@pages/webpack` |
|
||||
|
||||
Определи реальные flags scripts до выбора mode. Наличие Pages Router не означает автоматически Webpack: выбирай target по сборщику проекта.
|
||||
|
||||
## Структура и конфиг
|
||||
|
||||
```text
|
||||
src/ui/file-manager/svg-sprite/
|
||||
├── icons/
|
||||
│ ├── check.svg
|
||||
│ └── folder.svg
|
||||
└── svg-sprite.config.ts
|
||||
```
|
||||
|
||||
Установи пакет как development dependency:
|
||||
|
||||
```bash
|
||||
npm install --save-dev @gromlab/svg-sprites
|
||||
```
|
||||
|
||||
```ts
|
||||
import { defineSpriteConfig } from '@gromlab/svg-sprites'
|
||||
|
||||
export default defineSpriteConfig({
|
||||
mode: 'next@pages/webpack',
|
||||
name: 'file-manager',
|
||||
description: 'Иконки файлового менеджера',
|
||||
inputFolder: './icons',
|
||||
inputFiles: ['../../../../shared/icons/check.svg'],
|
||||
})
|
||||
```
|
||||
|
||||
Каталог с config выбирает проект конкретного спрайта и не обязан быть module/feature-каталогом; каждый config описывает один из потенциально многих спрайтов приложения.
|
||||
|
||||
Все source paths разрешаются от каталога конфига. `inputFolder` по умолчанию `./icons`, сканирование не рекурсивно. `inputFiles` объединяется с папкой. Один и тот же путь дедуплицируется, но два разных `check.svg` конфликтуют. Явная отсутствующая папка считается ошибкой даже при заполненном `inputFiles`.
|
||||
|
||||
`name` задаётся в kebab-case и определяет публичные имена. `FileManagerIcon` ниже — только пример generated-имени из `name: 'file-manager'`. Next modes всегда создают `stack`, не `symbol`.
|
||||
|
||||
## Команды
|
||||
|
||||
Пример lifecycle для Webpack:
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts",
|
||||
"predev": "npm run sprite:file-manager",
|
||||
"prebuild": "npm run sprite:file-manager",
|
||||
"pretypecheck": "npm run sprite:file-manager"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Если pre-scripts уже существуют, добавь генерацию в текущую цепочку. Generated imports отсутствуют в Git, поэтому генерация должна предшествовать TypeScript и Next compilation. Для Turbopack замени mode целиком на `next@pages/turbopack`. Для первой генерации запусти `npm run sprite:file-manager`.
|
||||
|
||||
## Использование в Pages Router
|
||||
|
||||
```tsx
|
||||
import { FileManagerIcon } from '@/ui/file-manager/svg-sprite'
|
||||
|
||||
export default function FilesPage() {
|
||||
return <FileManagerIcon icon="folder" className="icon" aria-label="Папка" />
|
||||
}
|
||||
|
||||
export function getServerSideProps() {
|
||||
return { props: {} }
|
||||
}
|
||||
```
|
||||
|
||||
`width` и `height` в JSX необязательны: размер можно задать CSS-классом или через `wrapped`. Компонент работает при SSR, SSG и client navigation. Он использует `new URL('./sprite.svg', import.meta.url).href`, чтобы Next выпустил внешний hashed asset. Не копируй generated SVG в `public` и не конструируй URL вручную.
|
||||
|
||||
Импортируй компонент и типы из локального пользовательского `svg-sprite/index.ts`, который переэкспортирует `.svg-sprite`. Не редактируй `.svg-sprite` и созданный `.gitignore`.
|
||||
|
||||
## SpriteViewer
|
||||
|
||||
Pages Router компоненты выполняются и на клиенте, поэтому отдельная директива `'use client'` не нужна:
|
||||
|
||||
```tsx
|
||||
import { SpriteViewer } from '@gromlab/svg-sprites/react'
|
||||
|
||||
const sources = [
|
||||
() => import('@/ui/file-manager/svg-sprite/manifest'),
|
||||
() => import('@/ui/navigation/svg-sprite/manifest'),
|
||||
]
|
||||
|
||||
export default function SpritesPage() {
|
||||
return <SpriteViewer sources={sources} title="Иконки проекта" />
|
||||
}
|
||||
```
|
||||
|
||||
Используй literal imports и размещай страницу только как debug/internal tool. Если page участвует в production routing, ограничь доступ средствами самого приложения.
|
||||
|
||||
## Проверка
|
||||
|
||||
```bash
|
||||
npm run sprite:file-manager
|
||||
npm run typecheck
|
||||
```
|
||||
|
||||
Если менялись target или Next build/deployment pipeline либо диагностируется runtime, запусти production build проекта, настроенный на выбранный сборщик:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
После обязательных генерации и typecheck проверь:
|
||||
|
||||
- `.svg-sprite/svg-sprite.manifest.js` содержит `next@pages/turbopack` либо `next@pages/webpack`;
|
||||
- `getServerSideProps`/`getStaticProps` не импортируют package React Viewer entry.
|
||||
|
||||
После условного production build и только при наличии браузерных инструментов проверь SSR route и переход на него:
|
||||
|
||||
- initial HTML содержит `.svg#id`;
|
||||
- SVG URL отвечает успешно и не является `data:`, `file:` или `blob:`;
|
||||
- после client navigation иконка сохраняет тот же корректный URL;
|
||||
- gradients, masks и цвета исследованы по [complex-svg.md](complex-svg.md); не утверждай визуальный или a11y результат без доступных инструментов.
|
||||
|
||||
## Типовые ошибки
|
||||
|
||||
- В mode указан `next@app/...`: модуль может сгенерироваться, но manifest и контракт target неверны; используй `next@pages/...`.
|
||||
- Command и mode выбирают разные bundler: используй соответствующую build-команду проекта и перегенерируй.
|
||||
- Viewer manifest не попадает в chunk: путь `import()` должен быть строковым литералом и существовать до Next build.
|
||||
- Иконка есть после full reload, но пропадает при переходе: проверь доступность внешнего asset с `basePath`, `assetPrefix` и production origin.
|
||||
- `Refusing to overwrite a user file`: sprite-каталог содержит неуправляемый файл с зарезервированным именем; перенеси его.
|
||||
- Имя иконки отсутствует в типе: папка не сканируется рекурсивно; перемести SVG на первый уровень либо добавь точный путь в `inputFiles`.
|
||||
|
||||
Для программной генерации используй [programmatic-api.md](programmatic-api.md).
|
||||
@@ -18,23 +18,28 @@ await generateSprite('src/ui/icons/svg-sprite.config.ts')
|
||||
await generateSprite('src/ui/icons', {
|
||||
mode: 'react@vite',
|
||||
name: 'app',
|
||||
inputFolder: './icons',
|
||||
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',
|
||||
inputFiles: ['../../shared/search.svg'],
|
||||
input: [
|
||||
'../../shared/icons/**/*.svg',
|
||||
'!../../shared/icons/legacy-*.svg',
|
||||
],
|
||||
transform: { addTransition: false },
|
||||
})
|
||||
```
|
||||
|
||||
Порядок: `defaults → config → API overrides`. `transform` объединяется по отдельным полям; переданный `inputFiles` заменяет массив из config.
|
||||
Порядок: `defaults → config → API overrides`. `transform` объединяется по отдельным полям; переданный `input` заменяет значение из config.
|
||||
|
||||
Специализированные `generateReactSprite` и `generateNextSprite` оставлены как совместимые обёртки, но для нового кода предпочитай `generateSprite`.
|
||||
|
||||
|
||||
@@ -1,166 +0,0 @@
|
||||
# React с Vite: операционный reference
|
||||
|
||||
## Когда открывать
|
||||
|
||||
Открывай этот документ, если проект использует React без Next.js и собирается Vite, либо если generated-компонент содержит импорт `sprite.svg?no-inline`. Не применяй этот target к Webpack: для него открой [react-webpack.md](react-webpack.md).
|
||||
|
||||
## Сначала установи контекст
|
||||
|
||||
1. Проверь `package.json`: должны быть React, Vite и фактические команды `dev`, `build`, `typecheck`.
|
||||
2. Найди существующие `svg-sprite.config.ts` и scripts с `svg-sprites`. Не создавай второй каталог для уже существующего спрайта.
|
||||
3. Выбери целевой каталог для конкретного спрайта. Это не обязан быть каталог module или feature: генератор принимает каталог с config, а не путь к самому config или `icons/`.
|
||||
4. Не редактируй вручную `.svg-sprite` и локальный `.gitignore`: ими владеет генератор. Корневой `index.ts` принадлежит приложению.
|
||||
|
||||
Минимальная структура:
|
||||
|
||||
```text
|
||||
src/ui/file-manager/svg-sprite/
|
||||
├── icons/
|
||||
│ ├── check.svg
|
||||
│ └── folder.svg
|
||||
└── svg-sprite.config.ts
|
||||
```
|
||||
|
||||
## Настройка
|
||||
|
||||
Установи пакет как development dependency:
|
||||
|
||||
```bash
|
||||
npm install --save-dev @gromlab/svg-sprites
|
||||
```
|
||||
|
||||
`src/ui/file-manager/svg-sprite/svg-sprite.config.ts`:
|
||||
|
||||
```ts
|
||||
import { defineSpriteConfig } from '@gromlab/svg-sprites'
|
||||
|
||||
export default defineSpriteConfig({
|
||||
mode: 'react@vite',
|
||||
name: 'file-manager',
|
||||
description: 'Иконки файлового менеджера',
|
||||
inputFolder: './icons',
|
||||
inputFiles: ['../../../../shared/icons/check.svg'],
|
||||
})
|
||||
```
|
||||
|
||||
- Каждый такой config описывает один конкретный спрайт; в приложении может быть много независимых config и спрайтов.
|
||||
- Все пути в конфиге разрешаются относительно каталога `svg-sprite.config.ts`.
|
||||
- `inputFolder` по умолчанию равен `./icons`; сканирование папки не рекурсивно и включает файлы с окончанием `.svg`.
|
||||
- `inputFolder` и `inputFiles` объединяются, одинаковый абсолютный путь дедуплицируется.
|
||||
- Если `inputFiles` заполнен, а неявного `./icons` нет, генерация работает только по списку. Явно заданная отсутствующая `inputFolder` всегда является ошибкой.
|
||||
- Разные файлы с одинаковым basename, например два `check.svg`, конфликтуют как одно публичное имя иконки.
|
||||
- `name` должен быть kebab-case и начинаться с латинской буквы. `FileManagerIcon`, `FileManagerIconName` и `fileManagerIconNames` ниже — только пример generated-имён для `name: 'file-manager'`.
|
||||
- React preset всегда создаёт формат `stack`; выбрать `symbol` здесь нельзя.
|
||||
|
||||
## Команда и scripts
|
||||
|
||||
Добавь локальный CLI в `package.json` и запускай до процессов, которым нужны generated imports:
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts",
|
||||
"predev": "npm run sprite:file-manager",
|
||||
"prebuild": "npm run sprite:file-manager",
|
||||
"pretypecheck": "npm run sprite:file-manager"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Если в проекте уже есть `predev` или `prebuild`, объедини команды в существующем orchestration вместо перезаписи script.
|
||||
|
||||
Для первой генерации запусти `npm run sprite:file-manager`.
|
||||
|
||||
## Использование
|
||||
|
||||
Импортируй только публичный локальный entry:
|
||||
|
||||
```tsx
|
||||
import { FileManagerIcon, fileManagerIconNames } from './svg-sprite'
|
||||
import type { FileManagerIconName, FileManagerIconStyle } from './svg-sprite'
|
||||
|
||||
const colorStyle: FileManagerIconStyle = {
|
||||
'--icon-color-1': '#2563eb',
|
||||
}
|
||||
|
||||
export function FolderIcon({ icon }: { icon: FileManagerIconName }) {
|
||||
return <FileManagerIcon icon={icon} className="icon" style={colorStyle} />
|
||||
}
|
||||
|
||||
export const availableIcons = fileManagerIconNames
|
||||
```
|
||||
|
||||
`width` и `height` в JSX необязательны: размер можно задать CSS-классом. Без `wrapped` компонент рендерит `<svg>` и принимает SVG attributes. С `wrapped={true}` корнем становится `<span>`, а внутренний SVG занимает его ширину и высоту:
|
||||
|
||||
```tsx
|
||||
<FileManagerIcon icon="folder" wrapped className="iconBox" />
|
||||
```
|
||||
|
||||
Для обычного импорта используй пользовательский barrel с `export * from './.svg-sprite'`; остальные файлы скрытого каталога считаются внутренними.
|
||||
|
||||
## Нюанс target
|
||||
|
||||
Vite target генерирует статический импорт:
|
||||
|
||||
```ts
|
||||
import spriteUrl from './sprite.svg?no-inline'
|
||||
```
|
||||
|
||||
Query `?no-inline` обязателен: он не даёт Vite превратить небольшой SVG в data URL. Не удаляй query и не копируй generated SVG в `public`; Vite должен выпустить отдельный asset с content hash.
|
||||
|
||||
Для низкоуровневого `<use>` применяй тот же механизм:
|
||||
|
||||
```tsx
|
||||
import spriteUrl from './svg-sprite/.svg-sprite/sprite.svg?no-inline'
|
||||
|
||||
<svg className="icon">
|
||||
<use href={`${spriteUrl}#check`} />
|
||||
</svg>
|
||||
```
|
||||
|
||||
Ручной fragment `#check` безопасен только для имён вида `^[a-zA-Z][a-zA-Z0-9_-]*$`. Для пробелов и других символов generated-компонент использует стабильный hash ID; точный ID находится в `.svg-sprite/svg-sprite.manifest.js`.
|
||||
|
||||
## SpriteViewer
|
||||
|
||||
После генерации добавь Viewer только на debug-маршрут:
|
||||
|
||||
```tsx
|
||||
import { SpriteViewer } from '@gromlab/svg-sprites/react'
|
||||
import type { SpriteManifestModule } from '@gromlab/svg-sprites/react'
|
||||
|
||||
const sources = import.meta.glob<SpriteManifestModule>(
|
||||
'/src/**/svg-sprite/.svg-sprite/svg-sprite.manifest.js',
|
||||
)
|
||||
|
||||
export function IconsDebugPage() {
|
||||
return <SpriteViewer sources={sources} title="Иконки проекта" />
|
||||
}
|
||||
```
|
||||
|
||||
Аргумент `import.meta.glob` должен оставаться строковым литералом. Генерация обязана завершиться до старта Vite, иначе новые manifests не попадут в glob.
|
||||
|
||||
## Проверка результата
|
||||
|
||||
```bash
|
||||
npm run sprite:file-manager
|
||||
npm run typecheck
|
||||
```
|
||||
|
||||
Это быстрые обязательные проверки. Проверь generated-файлы статически:
|
||||
|
||||
- пользовательский `index.ts` переэкспортирует компонент, props, style, union имени и runtime-массив из `.svg-sprite`;
|
||||
- `.svg-sprite/svg-sprite.manifest.js` содержит `target: "vite"`, `format: "stack"` и ожидаемое число иконок;
|
||||
|
||||
Если менялись target, asset pipeline или диагностируется runtime, дополнительно запусти production build. При наличии браузерных инструментов проверь Network: отдельный `.svg` asset вместо `data:image/svg+xml`, успешный URL и `<use href="...svg#...">`. Для сложных цветов, `defs` и размеров следуй [complex-svg.md](complex-svg.md), не утверждая визуальный или a11y результат без доступных инструментов.
|
||||
|
||||
## Типовые ошибки
|
||||
|
||||
- Config не найден: передай полный путь к существующему `.ts`, `.js` или `.json` config-файлу.
|
||||
- `Sprite mode is required`: добавь `mode: 'react@vite'` в config либо передай `--mode react@vite`.
|
||||
- Иконки нет в autocomplete: проверь case-sensitive окончание `.svg`, нерекурсивное расположение и повторно запусти генерацию до typecheck.
|
||||
- `Refusing to overwrite a user file`: не удаляй marker и не обходи writer; перенеси пользовательский файл или выбери другой sprite-каталог.
|
||||
- Viewer пуст: проверь строковый glob, существование `.svg-sprite/svg-sprite.manifest.js` и порядок запуска `predev`.
|
||||
- SVG оказался inline: проверь, что модуль сгенерирован target `vite` и импорт сохранил `?no-inline`.
|
||||
- TypeScript не разрешает package subpath: используй TypeScript 5+ и `moduleResolution: "bundler"`, `"node16"` или `"nodenext"`.
|
||||
|
||||
Для запуска без CLI используй [programmatic-api.md](programmatic-api.md).
|
||||
@@ -1,152 +0,0 @@
|
||||
# React с Webpack 5: операционный reference
|
||||
|
||||
## Когда открывать
|
||||
|
||||
Открывай этот документ для React-приложения на Webpack 5, когда SVG должен проходить через Asset Modules. Если проект собирается Vite, используй [react-vite.md](react-vite.md). Для Next.js не выбирай `react@webpack`: router-specific targets описаны в [next-app.md](next-app.md) и [next-pages.md](next-pages.md).
|
||||
|
||||
## Диагностика окружения до изменений
|
||||
|
||||
1. Подтверди Webpack major version 5 в `package.json` или lockfile.
|
||||
2. Изучи `module.rules` для `.svg`, `output.publicPath`, dev-server и существующие asset conventions.
|
||||
3. Найди существующие `svg-sprite.config.ts` и scripts генерации.
|
||||
4. Выбери каталог проекта конкретного спрайта. Он не обязан совпадать с module или feature-каталогом. Команда принимает каталог с config, не файл config и не папку иконок.
|
||||
|
||||
Минимальная структура:
|
||||
|
||||
```text
|
||||
src/ui/file-manager/svg-sprite/
|
||||
├── icons/
|
||||
│ ├── check.svg
|
||||
│ └── folder.svg
|
||||
└── svg-sprite.config.ts
|
||||
```
|
||||
|
||||
## Конфигурация и генерация
|
||||
|
||||
Установи пакет как development dependency:
|
||||
|
||||
```bash
|
||||
npm install --save-dev @gromlab/svg-sprites
|
||||
```
|
||||
|
||||
```ts
|
||||
import { defineSpriteConfig } from '@gromlab/svg-sprites'
|
||||
|
||||
export default defineSpriteConfig({
|
||||
mode: 'react@webpack',
|
||||
name: 'file-manager',
|
||||
description: 'Иконки файлового менеджера',
|
||||
inputFolder: './icons',
|
||||
inputFiles: ['../../../../shared/icons/check.svg'],
|
||||
})
|
||||
```
|
||||
|
||||
Каждый config описывает один из потенциально многих независимых спрайтов приложения.
|
||||
|
||||
Пути считаются от `svg-sprite.config.ts`. Папка сканируется только на первом уровне. `inputFolder` и `inputFiles` объединяются; одинаковый путь дедуплицируется, но одинаковые basename у разных файлов вызывают конфликт ID. Неявный `./icons` можно не создавать, если заполнен `inputFiles`; явно заданная отсутствующая папка является ошибкой. `FileManagerIcon` ниже — только пример generated-имени из `name: 'file-manager'`.
|
||||
|
||||
Рекомендуемые lifecycle hooks:
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"sprite:file-manager": "svg-sprites src/ui/file-manager/svg-sprite/svg-sprite.config.ts",
|
||||
"predev": "npm run sprite:file-manager",
|
||||
"prebuild": "npm run sprite:file-manager",
|
||||
"pretypecheck": "npm run sprite:file-manager"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Не затирай уже существующие pre-scripts: включи генерацию в их текущую цепочку. Для первой генерации запусти `npm run sprite:file-manager`. React preset всегда выпускает `stack`.
|
||||
|
||||
## Публичный компонент
|
||||
|
||||
```tsx
|
||||
import { FileManagerIcon } from './svg-sprite'
|
||||
|
||||
export function OpenFolderButton() {
|
||||
return (
|
||||
<button type="button">
|
||||
<FileManagerIcon icon="folder" className="icon" aria-hidden />
|
||||
Открыть
|
||||
</button>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
`width` и `height` в JSX необязательны: размер можно задать CSS-классом или через `wrapped`. Пользовательский barrel также экспортирует `FileManagerIconProps`, `FileManagerIconStyle`, `FileManagerIconName` и `fileManagerIconNames`. Не редактируй `.svg-sprite` или созданный `.gitignore`.
|
||||
|
||||
## Нюанс Webpack target
|
||||
|
||||
Generated-компонент получает URL только через статическое выражение:
|
||||
|
||||
```ts
|
||||
const spriteUrl = new URL('./sprite.svg', import.meta.url).href
|
||||
```
|
||||
|
||||
Webpack 5 распознаёт его как Asset Module и заменяет на публичный URL отдельного SVG. Для корректной обработки:
|
||||
|
||||
- не оборачивай путь в переменную и не меняй generated expression;
|
||||
- убедись, что Babel/TypeScript не преобразует `import.meta.url` до Webpack;
|
||||
- не позволяй `@svgr/webpack`, `svg-inline-loader`, `raw-loader` или общему SVG rule перехватить `svg-sprite/.svg-sprite/sprite.svg`;
|
||||
- при custom rule либо исключи generated sprite из component/raw loader, либо добавь отдельное правило `type: 'asset/resource'`;
|
||||
- проверь `output.publicPath`, особенно при CDN, subpath deployment и dev-server.
|
||||
|
||||
Пример отдельного правила, если существующий loader перехватывает все SVG:
|
||||
|
||||
```js
|
||||
{
|
||||
test: /svg-sprite[\\/]\.svg-sprite[\\/]sprite\.svg$/,
|
||||
type: 'asset/resource',
|
||||
}
|
||||
```
|
||||
|
||||
Согласуй это правило с текущим конфигом проекта: не добавляй дублирующий matcher, если стандартные Asset Modules уже обрабатывают `new URL` правильно.
|
||||
|
||||
## SpriteViewer
|
||||
|
||||
Webpack не предоставляет `import.meta.glob`. Передай статические lazy imports со строковыми литералами:
|
||||
|
||||
```tsx
|
||||
import { SpriteViewer } from '@gromlab/svg-sprites/react'
|
||||
|
||||
const sources = [
|
||||
() => import('./ui/file-manager/svg-sprite/manifest'),
|
||||
() => import('./ui/navigation/svg-sprite/manifest'),
|
||||
]
|
||||
|
||||
export function IconsDebugPage() {
|
||||
return <SpriteViewer sources={sources} title="Иконки проекта" />
|
||||
}
|
||||
```
|
||||
|
||||
Динамически собранный путь `import(path)` не подходит: Webpack не сможет точно связать manifests и assets. Viewer должен быть доступен только на debug/internal route.
|
||||
|
||||
## Проверка
|
||||
|
||||
```bash
|
||||
npm run sprite:file-manager
|
||||
npm run typecheck
|
||||
```
|
||||
|
||||
Это быстрые обязательные проверки. После генерации проверь:
|
||||
|
||||
- `.svg-sprite/svg-sprite.manifest.js` содержит `target: "webpack"` и `format: "stack"`;
|
||||
- generated component содержит `new URL('./sprite.svg', import.meta.url).href`;
|
||||
|
||||
Production build и браузер/Network нужны дополнительно, если менялись target, Webpack asset rules, `publicPath`/deployment pipeline или диагностируется runtime. Тогда проверь отдельный hashed SVG asset, HTTP(S)-URL `.svg#id`, content type, fragment ID и корректный CDN/subpath URL. Не утверждай визуальную или a11y корректность без доступных инструментов.
|
||||
|
||||
Для иконок с gradients, filters, masks или внутренним CSS выполни проверки из [complex-svg.md](complex-svg.md).
|
||||
|
||||
## Типовые ошибки
|
||||
|
||||
- `Unsupported React target`: программному API передано не `'webpack'`; CLI mode должен быть ровно `react@webpack`.
|
||||
- Webpack пытается отрендерить SVG как React-компонент: generated sprite попал под SVGR rule; исключи его или приоритизируй `asset/resource`.
|
||||
- URL ведёт на неверный host/subpath: исправь `output.publicPath` и настройки runtime deployment, не generated-файл.
|
||||
- `import.meta` не поддержан: проверь, что сборка действительно Webpack 5 и промежуточный transpiler сохраняет выражение.
|
||||
- Viewer не загружает manifest: проверь literal path, chunk loading и наличие генерации до компиляции.
|
||||
- `Refusing to overwrite a user file`: каталог уже содержит пользовательский `.gitignore` или файл в `.svg-sprite`; перенеси его, не обходи защиту.
|
||||
- Иконка не меняет цвет: используй `color` для монохромной либо `--icon-color-N` через `<svg><use>`; CSS страницы не проникает внутрь `<img>`.
|
||||
|
||||
Для custom build orchestration см. [programmatic-api.md](programmatic-api.md).
|
||||
Reference in New Issue
Block a user