diff --git a/.changeset/fuzzy-camels-sip.md b/.changeset/fuzzy-camels-sip.md
new file mode 100644
index 0000000..3c78a45
--- /dev/null
+++ b/.changeset/fuzzy-camels-sip.md
@@ -0,0 +1,5 @@
+---
+'@neodx/svg': minor
+---
+
+Introduce new `metadata` API
diff --git a/README.md b/README.md
index e1c2edf..53d7ddf 100644
--- a/README.md
+++ b/README.md
@@ -39,9 +39,9 @@ We have a some ideas for future development, so stay tuned and feel free to requ
### [@neodx/svg](./libs/svg)
-Are you converting every SVG icon to React component with SVGR or something similar? It's so ease to use!
+Are you converting every SVG icon to a React component with SVGR or something similar? It's so ease to use!
-But wait, did you know that SVG sprites are native approach for icons? It's even easier to use!
+But wait; did you know that SVG sprites are a native approach for icons? It's even easier to use!
```typescript jsx
import { Icon } from '@/shared/ui';
@@ -75,7 +75,7 @@ export default defineConfig({
svg({
root: 'assets',
output: 'public',
- definitions: 'src/shared/ui/icon/sprite.h.ts'
+ metadata: 'src/shared/ui/icon/sprite.gen.ts'
})
]
});
@@ -94,7 +94,7 @@ export default {
svg({
root: 'assets',
output: 'public',
- definitions: 'src/shared/ui/icon/sprite.h.ts'
+ metadata: 'src/shared/ui/icon/sprite.gen.ts'
})
]
};
@@ -113,7 +113,7 @@ export default {
svg({
root: 'assets',
output: 'public',
- definitions: 'src/shared/ui/icon/sprite.h.ts'
+ metadata: 'src/shared/ui/icon/sprite.gen.ts'
})
]
};
@@ -132,7 +132,7 @@ export default {
svg({
root: 'assets',
output: 'public',
- definitions: 'src/shared/ui/icon/sprite.h.ts'
+ metadata: 'src/shared/ui/icon/sprite.gen.ts'
})
]
};
@@ -144,7 +144,7 @@ export default {
CLI
```shell
-npx @neodx/svg --group --root assets --output public --definition src/shared/ui/icon/sprite.h.ts
+npx @neodx/svg --group --root assets --output public --definition src/shared/ui/icon/sprite.gen.ts
# --root - root folder with SVGs
# --group - group icons by folders (assets/common/add.svg -> common/add, assets/other/cut.svg -> other/cut)
# --output (-o) - output folder for sprites
@@ -165,7 +165,7 @@ await buildSprites({
root: 'assets',
input: '**/*.svg',
output: 'public',
- definition: 'src/shared/ui/icon/sprite.h.ts'
+ metadata: 'src/shared/ui/icon/sprite.gen.ts'
});
```
@@ -185,7 +185,7 @@ src/
shared/
ui/
icon/
-+ sprite.h.ts // sprite definitions - types, metadata, etc.
++ sprite.gen.ts // sprite definitions - types, metadata, etc.
public/
+ sprite/
+ common.svg
@@ -208,7 +208,7 @@ Having trouble finding a suitable logging library because they're too heavy, pla
I faced the same issues, which led me to create `@neodx/log`.
It's simple, efficient, and avoids most critical drawbacks.
-Furthermore, it's easily replaceable and extensible, making it the great fit for your development needs.
+Furthermore, it's easily replaceable and extensible, making it a great fit for your development needs.
diff --git a/apps/docs/svg/frameworks-and-bundlers.md b/apps/docs/svg/frameworks-and-bundlers.md
index d98efb3..48c0d25 100644
--- a/apps/docs/svg/frameworks-and-bundlers.md
+++ b/apps/docs/svg/frameworks-and-bundlers.md
@@ -18,7 +18,7 @@ export default defineConfig({
root: 'assets',
group: true,
output: 'public',
- definitions: 'src/shared/ui/icon/sprite.h.ts',
+ metadata: 'src/shared/ui/icon/sprite.gen.ts',
resetColors: {
replaceUnknown: 'currentColor'
}
diff --git a/examples/svg-magic-with-figma-export/src/shared/ui/icon/icon.tsx b/examples/svg-magic-with-figma-export/src/shared/ui/icon/icon.tsx
index 910b6ad..a456d71 100644
--- a/examples/svg-magic-with-figma-export/src/shared/ui/icon/icon.tsx
+++ b/examples/svg-magic-with-figma-export/src/shared/ui/icon/icon.tsx
@@ -21,7 +21,7 @@ export function Icon({ name, className, viewBox, ...props }: IconProps) {
aria-hidden
{...props}
>
-
+
);
}
diff --git a/examples/svg-magic-with-figma-export/vite.config.ts b/examples/svg-magic-with-figma-export/vite.config.ts
index ca9481b..bc084c2 100644
--- a/examples/svg-magic-with-figma-export/vite.config.ts
+++ b/examples/svg-magic-with-figma-export/vite.config.ts
@@ -14,10 +14,10 @@ export default defineConfig({
root: 'assets/icons',
output: 'public',
group: true,
+ metadata: 'src/shared/ui/icon/sprite.gen.ts',
resetColors: {
replace: ['#6C707E', '#A8ADBD', '#818594']
- },
- definitions: 'src/shared/ui/icon/sprite.gen.ts'
+ }
})
]
});
diff --git a/examples/svg-next/next.config.mjs b/examples/svg-next/next.config.mjs
index e4d31dd..f27a263 100644
--- a/examples/svg-next/next.config.mjs
+++ b/examples/svg-next/next.config.mjs
@@ -10,7 +10,7 @@ const nextConfig = {
svg({
root: 'assets',
output: 'public',
- definitions: 'src/shared/ui/icon/sprite.gen.ts',
+ metadata: 'src/shared/ui/icon/sprite.gen.ts',
resetColors: {
replaceUnknown: 'currentColor'
}
diff --git a/examples/svg-next/src/shared/ui/icon/icon.tsx b/examples/svg-next/src/shared/ui/icon/icon.tsx
index 60de46e..3f3e73f 100644
--- a/examples/svg-next/src/shared/ui/icon/icon.tsx
+++ b/examples/svg-next/src/shared/ui/icon/icon.tsx
@@ -17,7 +17,7 @@ export function Icon({ name, className, viewBox, ...props }: IconProps) {
aria-hidden
{...props}
>
-
+
);
}
diff --git a/examples/svg-vite/README.md b/examples/svg-vite/README.md
index d0a61cf..ea66613 100644
--- a/examples/svg-vite/README.md
+++ b/examples/svg-vite/README.md
@@ -1,10 +1,9 @@
# Example of using `@neodx/svg` Vite plugin
-> **Warning** In this example was used `experimentalRuntime` option and advanced `fileName` feature, API will be changed in the nearest future.
-
This example shows how to use `@neodx/svg` as Vite plugin and simple step-by-step setup for React.
-In the addition you can see how to use multicolored icons with TailwindCSS and CSS variable (it's not very pleasant, but it works 🌝).
+In addition, you can see how to use multicolored icons with TailwindCSS and CSS variable
+(it's not very pleasant, but it works 🌝).
![result](./docs/result.png)
@@ -37,7 +36,14 @@ export default defineConfig(({ command }) => ({
root: 'assets', // Root folder for SVG files, all source paths will be relative to this folder
group: true, // Group SVG files by folder
output: 'public', // Output folder for generated files
- definitions: 'src/shared/ui/icon/sprite.gen.ts', // Output file for generated TypeScript definitions
+ metadata: {
+ path: 'src/shared/ui/icon/sprite.gen.ts', // Output file for generated TypeScript definitions
+ runtime: {
+ // Generate additional runtime information
+ size: true,
+ viewBox: true
+ }
+ },
resetColors: {
replace: ['#000', '#eee', '#6C707E'], // Resets all known colors to `currentColor`
replaceUnknown: 'var(--icon-color)' // Replaces unknown colors with custom CSS variable
@@ -47,14 +53,14 @@ export default defineConfig(({ command }) => ({
}));
```
-## Create Icon component and describe basic styles
+## Create an Icon component and describe basic styles
[shared/ui/icon/icon.tsx](./src/shared/ui/icon/icon.tsx):
```tsx
import clsx from 'clsx';
import type { SVGProps } from 'react';
-import type { SpritesMap } from './sprite.gen';
+import { SPRITES_META, type SpritesMap } from './sprite.gen';
// Merging all icons as `SPRITE_NAME/ICON_NAME`
export type SpriteKey = {
@@ -66,18 +72,30 @@ export interface IconProps extends Omit
, 'name' | 'type'
}
export function Icon({ name, className, viewBox, ...props }: IconProps) {
- const [spriteName, iconName] = name.split('/');
+ const [spriteName, iconName] = name.split('/') as [
+ keyof SpritesMap,
+ SpritesMap[keyof SpritesMap]
+ ];
+ const { filePath, items } = SPRITES_META[spriteName];
+ // @ts-expect-error mixed structures are confusing TS
+ const { viewBox, width, height } = items[iconName];
+ const rect = width === height ? 'xy' : width > height ? 'x' : 'y';
return (
-
+
);
}
@@ -100,7 +118,19 @@ export function Icon({ name, className, viewBox, ...props }: IconProps) {
@layer components {
/* Our base class for all icons */
.icon {
- @apply select-none fill-current w-[1em] h-[1em] inline-block text-inherit box-content;
+ @apply select-none fill-current inline-block text-inherit box-content;
+ }
+
+ .icon[data-icon-aspect-ratio='xy'] {
+ @apply w-[1em] h-[1em];
+ }
+
+ .icon[data-icon-aspect-ratio='x'] {
+ @apply w-[1em];
+ }
+
+ .icon[data-icon-aspect-ratio='y'] {
+ @apply h-[1em];
}
}
```
@@ -125,8 +155,8 @@ Under this example I want to cover all planned features of `@neodx/svg`, you can
- [x] Colors: replace known to CSS variables
- [x] Colors: exclude specific icons
- [x] Colors: exclude specific colors
-- [ ] Non-standard sizes: generate `viewBox` and `width`/`height` attributes
-- [ ] Non-standard sizes: example of enhanced `Icon` component
+- [x] Non-standard sizes: generate `viewBox` and `width`/`height` attributes
+- [x] Non-standard sizes: example of enhanced `Icon` component
- [ ] Inline SVG: auto-detection of internal references
- [ ] Inline SVG: injection into HTML
- [ ] Remove unnecessary attributes
diff --git a/examples/svg-vite/src/shared/ui/icon/icon.tsx b/examples/svg-vite/src/shared/ui/icon/icon.tsx
index 3169351..3012a30 100644
--- a/examples/svg-vite/src/shared/ui/icon/icon.tsx
+++ b/examples/svg-vite/src/shared/ui/icon/icon.tsx
@@ -10,24 +10,30 @@ export interface IconProps extends Omit, 'name' | 'type'
name: IconName;
}
-export function Icon({ name, className, viewBox: viewBoxFromProps, ...props }: IconProps) {
+export function Icon({ name, className, ...props }: IconProps) {
const [spriteName, iconName] = name.split('/') as [
keyof SpritesMap,
SpritesMap[keyof SpritesMap]
];
const { filePath, items } = SPRITES_META[spriteName];
// TODO Fix types
- const { viewBox } = (items as any)[iconName] as { viewBox: string };
+ const { viewBox, width, height } = (items as any)[iconName] as any;
+ const rect = width === height ? 'xy' : width > height ? 'x' : 'y';
return (
-
+
);
}
diff --git a/examples/svg-vite/src/shared/ui/icon/sprite.gen.ts b/examples/svg-vite/src/shared/ui/icon/sprite.gen.ts
index e4ceefa..ecb13b0 100644
--- a/examples/svg-vite/src/shared/ui/icon/sprite.gen.ts
+++ b/examples/svg-vite/src/shared/ui/icon/sprite.gen.ts
@@ -425,115 +425,184 @@ export interface SpritesMap {
| 'zw';
logos: 'linkedin' | 'twitter';
}
-
export const SPRITES_META = {
common: {
filePath: 'common.76103a6c.svg',
items: {
add: {
- viewBox: '0 0 16 16'
+ viewBox: '0 0 16 16',
+ width: 16,
+ height: 16
},
'autoscroll-from-source': {
- viewBox: '0 0 16 16'
+ viewBox: '0 0 16 16',
+ width: 16,
+ height: 16
},
'autoscroll-to-source': {
- viewBox: '0 0 16 16'
+ viewBox: '0 0 16 16',
+ width: 16,
+ height: 16
},
checkmark: {
- viewBox: '0 0 16 16'
+ viewBox: '0 0 16 16',
+ width: 16,
+ height: 16
},
'chevron-down-large': {
- viewBox: '0 0 16 16'
+ viewBox: '0 0 16 16',
+ width: 16,
+ height: 16
},
'chevron-down': {
- viewBox: '0 0 16 16'
+ viewBox: '0 0 16 16',
+ width: 16,
+ height: 16
},
'chevron-left': {
- viewBox: '0 0 16 16'
+ viewBox: '0 0 16 16',
+ width: 16,
+ height: 16
},
'chevron-right': {
- viewBox: '0 0 16 16'
+ viewBox: '0 0 16 16',
+ width: 16,
+ height: 16
},
'chevron-up-large': {
- viewBox: '0 0 16 16'
+ viewBox: '0 0 16 16',
+ width: 16,
+ height: 16
},
'chevron-up': {
- viewBox: '0 0 16 16'
+ viewBox: '0 0 16 16',
+ width: 16,
+ height: 16
},
'close-small-hovered': {
- viewBox: '0 0 16 16'
+ viewBox: '0 0 16 16',
+ width: 16,
+ height: 16
},
'close-small': {
- viewBox: '0 0 16 16'
+ viewBox: '0 0 16 16',
+ width: 16,
+ height: 16
},
close: {
- viewBox: '0 0 16 16'
+ viewBox: '0 0 16 16',
+ width: 16,
+ height: 16
},
'collapse-all': {
- viewBox: '0 0 16 16'
+ viewBox: '0 0 16 16',
+ width: 16,
+ height: 16
},
copy: {
- viewBox: '0 0 16 16'
+ viewBox: '0 0 16 16',
+ width: 16,
+ height: 16
},
cut: {
- viewBox: '0 0 16 16'
+ viewBox: '0 0 16 16',
+ width: 16,
+ height: 16
},
delete: {
- viewBox: '0 0 16 16'
+ viewBox: '0 0 16 16',
+ width: 16,
+ height: 16
},
'double-color': {
- viewBox: '0 0 16 16'
+ viewBox: '0 0 16 16',
+ width: 16,
+ height: 16
},
down: {
- viewBox: '0 0 16 16'
+ viewBox: '0 0 16 16',
+ width: 16,
+ height: 16
},
download: {
- viewBox: '0 0 16 16'
+ viewBox: '0 0 16 16',
+ width: 16,
+ height: 16
},
edit: {
- viewBox: '0 0 16 16'
+ viewBox: '0 0 16 16',
+ width: 16,
+ height: 16
},
exit: {
- viewBox: '0 0 16 16'
+ viewBox: '0 0 16 16',
+ width: 16,
+ height: 16
},
'expand-all': {
- viewBox: '0 0 16 16'
+ viewBox: '0 0 16 16',
+ width: 16,
+ height: 16
},
export: {
- viewBox: '0 0 16 16'
+ viewBox: '0 0 16 16',
+ width: 16,
+ height: 16
},
'external-link': {
- viewBox: '0 0 16 16'
+ viewBox: '0 0 16 16',
+ width: 16,
+ height: 16
},
favourite: {
- viewBox: '0 0 48 48'
+ viewBox: '0 0 48 48',
+ width: 48,
+ height: 48
},
filter: {
- viewBox: '0 0 16 16'
+ viewBox: '0 0 16 16',
+ width: 16,
+ height: 16
},
groups: {
- viewBox: '0 0 16 16'
+ viewBox: '0 0 16 16',
+ width: 16,
+ height: 16
},
help: {
- viewBox: '0 0 16 16'
+ viewBox: '0 0 16 16',
+ width: 16,
+ height: 16
},
hide: {
- viewBox: '0 0 16 16'
+ viewBox: '0 0 16 16',
+ width: 16,
+ height: 16
},
history: {
- viewBox: '0 0 16 16'
+ viewBox: '0 0 16 16',
+ width: 16,
+ height: 16
},
'ide-update': {
- viewBox: '0 0 16 16'
+ viewBox: '0 0 16 16',
+ width: 16,
+ height: 16
},
import: {
- viewBox: '0 0 16 16'
+ viewBox: '0 0 16 16',
+ width: 16,
+ height: 16
},
layout: {
- viewBox: '0 0 16 16'
+ viewBox: '0 0 16 16',
+ width: 16,
+ height: 16
},
left: {
- viewBox: '0 0 16 16'
+ viewBox: '0 0 16 16',
+ width: 16,
+ height: 16
}
}
},
@@ -541,1165 +610,1939 @@ export const SPRITES_META = {
filePath: 'flags.49fa86bc.svg',
items: {
ac: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ad: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ae: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'af-emirate': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
af: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
afar: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ag: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ai: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
al: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
am: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
an: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ao: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'aq-true_south': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
aq: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ar: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
as: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
at: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'au-aboriginal': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'au-act': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'au-nsw': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'au-nt': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'au-qld': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'au-sa': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'au-tas': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'au-vic': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'au-wa': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
au: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
aw: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ax: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
az: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ba: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
bb: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
bd: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
be: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
bf: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
bg: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
bh: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
bi: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
bj: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
bl: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
bm: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
bn: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
bo: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'bq-bo': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'bq-sa': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'bq-se': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
bq: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
br: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
bs: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
bt: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
bv: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
bw: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
by: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
bz: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'ca-bc': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'ca-qc': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ca: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
cc: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
cd: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
cf: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
cg: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'ch-gr': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ch: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ci: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ck: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
cl: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
cm: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'cn-hk': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'cn-xj': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
cn: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
co: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
cp: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
cq: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
cr: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
cu: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
cv: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
cw: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
cx: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
cy: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
cz: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
de: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
dg: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
dj: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
dk: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
dm: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
do: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
dz: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ea: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
earth: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
east_african_federation: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
easter_island: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'ec-w': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ec: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ee: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
eg: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
eh: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
er: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'es-ar': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'es-ce': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'es-cn': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'es-ct': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'es-ga': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'es-ib': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'es-ml': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'es-pv': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'es-variant': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
es: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'et-or': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'et-ti': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
et: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
eu: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
european_union: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ewe: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
fi: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
fj: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
fk: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
fm: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
fo: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'fr-20r': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'fr-bre': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'fr-cp': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
fr: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
fx: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ga: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'gb-con': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'gb-eng': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'gb-nir': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'gb-ork': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'gb-sct': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'gb-wls': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
gb: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
gd: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'ge-ab': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ge: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
gf: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
gg: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
gh: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
gi: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
gl: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
gm: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
gn: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
gp: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
gq: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
gr: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
gs: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
gt: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
gu: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
guarani: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
gw: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
gy: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
hausa: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
hk: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
hm: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
hmong: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
hn: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
hr: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ht: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
hu: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ic: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'id-jb': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'id-jt': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
id: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ie: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
il: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
im: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'in-as': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'in-gj': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'in-ka': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'in-mn': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'in-mz': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'in-or': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'in-tg': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'in-tn': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
in: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
io: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'iq-kr': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
iq: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ir: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
is: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'it-23': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'it-82': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'it-88': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
it: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
je: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
jm: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
jo: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
jp: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
kanuri: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ke: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
kg: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
kh: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ki: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
kikuyu: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
km: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
kn: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
kongo: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
kp: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
kr: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
kw: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ky: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
kz: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
la: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
lb: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
lc: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
li: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
lk: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
lr: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ls: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
lt: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
lu: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
lv: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ly: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ma: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
malayali: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
maori: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
mars: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
mc: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
md: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
me: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
mf: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
mg: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
mh: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
mk: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ml: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
mm: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
mn: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
mo: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
mp: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'mq-old': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
mq: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
mr: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ms: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
mt: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
mu: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
mv: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
mw: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
mx: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
my: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
mz: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
na: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
nato: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
nc: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ne: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
nf: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ng: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ni: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'nl-fr': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
nl: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
no: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
northern_cyprus: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
np: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
nr: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
nu: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
nz: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
occitania: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
olympics: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
om: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
otomi: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
pa: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
pe: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
pf: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
pg: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ph: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'pk-jk': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'pk-sd': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
pk: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
pl: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
pm: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
pn: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
pr: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ps: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'pt-20': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'pt-30': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
pt: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
pw: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
py: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
qa: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
quechua: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
re: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ro: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
rs: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'ru-ba': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'ru-ce': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'ru-cu': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'ru-da': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'ru-dpr': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'ru-ko': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'ru-lpr': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'ru-ta': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'ru-ud': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ru: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
rw: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
sa: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
sami: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
sb: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
sc: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
sd: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
se: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
sg: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'sh-ac': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'sh-hl': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'sh-ta': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
sh: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
si: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
sj: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
sk: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
sl: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
sm: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
sn: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
so: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
somaliland: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
south_ossetia: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
soviet_union: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
sr: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ss: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
st: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
su: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
sv: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
sx: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
sy: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
sz: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ta: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
tc: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
td: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
tf: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
tg: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
th: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
tibet: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
tj: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
tk: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
tl: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
tm: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
tn: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
to: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
torres_strait_islands: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
tr: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
transnistria: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
tt: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
tv: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
tw: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
tz: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ua: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ug: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
uk: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
um: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
un: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
united_nations: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'us-ak': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'us-al': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'us-ar': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'us-az': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'us-ca': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'us-co': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'us-dc': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'us-fl': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'us-ga': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'us-hi': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'us-in': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'us-mo': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'us-ms': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'us-nc': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'us-nm': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'us-ri': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'us-tn': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
'us-tx': {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
us: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
uy: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
uz: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
va: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
vc: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ve: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
vg: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
vi: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
vn: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
vu: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
wf: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
wiphala: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ws: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
xk: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
xx: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
ye: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
yorubaland: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
yt: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
yu: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
za: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
zm: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
},
zw: {
- viewBox: '0 0 512 512'
+ viewBox: '0 0 512 512',
+ width: 512,
+ height: 512
}
}
},
@@ -1707,20 +2550,28 @@ export const SPRITES_META = {
filePath: 'logos.bab17578.svg',
items: {
linkedin: {
- viewBox: '0 0 140 34'
+ viewBox: '0 0 140 34',
+ width: 140,
+ height: 34
},
twitter: {
- viewBox: '0 0 248 204'
+ viewBox: '0 0 248 204',
+ width: 248,
+ height: 204
}
}
}
-} satisfies {
- [SpriteName in keyof SpritesMap]: {
+} satisfies Record<
+ string,
+ {
filePath: string;
- items: {
- [ItemName in SpritesMap[SpriteName]]: {
+ items: Record<
+ string,
+ {
viewBox: string;
- };
- };
- };
-};
+ width: number;
+ height: number;
+ }
+ >;
+ }
+>;
diff --git a/examples/svg-vite/src/shared/ui/index.css b/examples/svg-vite/src/shared/ui/index.css
index c9aff48..9518485 100644
--- a/examples/svg-vite/src/shared/ui/index.css
+++ b/examples/svg-vite/src/shared/ui/index.css
@@ -10,6 +10,18 @@
@layer components {
.icon {
- @apply select-none fill-current w-[1em] h-[1em] inline-block text-inherit box-content;
+ @apply select-none fill-current inline-block text-inherit box-content;
+ }
+
+ .icon[data-icon-aspect-ratio='xy'] {
+ @apply w-[1em] h-[1em];
+ }
+
+ .icon[data-icon-aspect-ratio='x'] {
+ @apply w-[1em];
+ }
+
+ .icon[data-icon-aspect-ratio='y'] {
+ @apply h-[1em];
}
}
diff --git a/examples/svg-vite/vite.config.ts b/examples/svg-vite/vite.config.ts
index e771391..be62801 100644
--- a/examples/svg-vite/vite.config.ts
+++ b/examples/svg-vite/vite.config.ts
@@ -12,8 +12,13 @@ export default defineConfig({
group: true,
output: 'public',
fileName: '{name}.{hash:8}.svg',
- experimentalRuntime: true,
- definitions: 'src/shared/ui/icon/sprite.gen.ts',
+ metadata: {
+ path: 'src/shared/ui/icon/sprite.gen.ts',
+ runtime: {
+ size: true,
+ viewBox: true
+ }
+ },
resetColors: {
exclude: [/^flags/, /^logos/],
replace: ['#000', '#eee', '#6C707E', '#313547'],
diff --git a/libs/svg/README.md b/libs/svg/README.md
index 7af36a5..b7e641f 100644
--- a/libs/svg/README.md
+++ b/libs/svg/README.md
@@ -5,11 +5,11 @@ Supercharge your icons ⚡️
## Motivation
Sprites are the most effective way to work with your SVG icons,
-but for some reason developers (vision from react world) prefer
-mostly bloated and ineffective - "compile" SVG to react component with inlined SVG content.
+but for some reason developers (vision from a React world) prefer
+mostly bloated and ineffective, "compile" SVG to react component with inlined SVG content.
Of course, we can use some external tools like https://svgsprit.es/ or some npm libraries,
-but that's not serious (if you know any alternatives - let me know, and I'll add links), developers need DX.
+but that's not serious (if you know any alternatives, let me know, and I'll add links), developers need DX.
In a ridiculous, but incredibly popular way, we don't have other solutions with the same DX.
@@ -23,11 +23,11 @@ is better than a super-efficient, but unusable setup with semi-manual generators
That's why we're here! 🥳
-- TypeScript support out of box - generated types and [information about your sprites](#content-based-hashes)
+- TypeScript support out of box - generated types and [information about your sprites](#-content-based-hashes-and-runtime-metadata-generation)
- [Built-in integrated plugins](#integrate-with-your-bundler) for all major bundlers: `vite`, `webpack`, `rollup`, `esbuild`, etc.
- Optional grouping by folders
- Optimization with svgo
-- [Automatically reset colors](#-powerful-colors-reset)
+- [Automatically reset colors](#-automatically-reset-colors)
- Powerful files selection
## Installation and usage
@@ -43,7 +43,8 @@ pnpm add -D @neodx/svg
### CLI (Not recommended)
-Currently, we don't recommend using CLI mode, because it's not flexible enough and requires extra setup, if you want to use it - see [CLI](#cli) section and [CLI Options API](#cli-options).
+Currently, we don't recommend using CLI mode because it's not flexible enough and requires extra setup
+if you want to use it - see [CLI](#cli) section and [CLI Options API](#cli-options).
```shell
yarn sprite --help
@@ -65,7 +66,7 @@ export default defineConfig({
root: 'assets',
group: true,
output: 'public',
- definitions: 'src/shared/ui/icon/sprite.gen.ts',
+ metadata: 'src/shared/ui/icon/sprite.gen.ts',
resetColors: {
replaceUnknown: 'currentColor'
}
@@ -98,7 +99,7 @@ export default {
svg({
root: 'assets',
output: 'public',
- definition: 'src/shared/ui/icon/sprite.gen.ts'
+ metadata: 'src/shared/ui/icon/sprite.gen.ts'
})
]
};
@@ -117,7 +118,7 @@ export default {
svg({
root: 'assets',
output: 'public',
- definition: 'src/shared/ui/icon/sprite.gen.ts'
+ metadata: 'src/shared/ui/icon/sprite.gen.ts'
})
]
};
@@ -136,7 +137,7 @@ export default {
svg({
root: 'assets',
output: 'public',
- definition: 'src/shared/ui/icon/sprite.gen.ts'
+ metadata: 'src/shared/ui/icon/sprite.gen.ts'
})
]
};
@@ -276,9 +277,9 @@ svg({
});
```
-### 🆕 ⚠️ Get content-based hashes in filenames with experimental runtime information
+### 🆕 Content-based hashes and runtime metadata generation
-> **Warning:** This feature is experimental and will be changed in the future.
+> **Note:** If you used `definitions` or `experimentalRuntime` options before, you need to update your configuration, see [Migration guide](#move-from-definitions-and-experimentalruntime-options-to-metadata-api).
By default, you will get the following sprites in your output:
@@ -295,7 +296,7 @@ the sprite filename won't be updated, which could result in an infinite cache.
To solve this issue and achieve content-based hashes in filenames, you need to take three steps:
1. Provide the `fileName` option with a `hash` variable (e.g. `fileName: "{name}.{hash:8}.svg"`)
-2. Enable the `experimentalRuntime` option to get information about the file path by sprite name during runtime
+2. Configure the `metadata` option to get additional information about the file path by sprite name during runtime
3. Update your `Icon` component (or whatever you use) to support the new runtime information
```typescript
@@ -305,7 +306,13 @@ export default defineConfig({
plugins: [
svg({
fileName: '{name}.{hash:8}.svg',
- experimentalRuntime: true
+ metadata: {
+ path: 'src/shared/ui/icon/sprite.gen.ts',
+ runtime: {
+ size: true,
+ viewBox: true
+ }
+ }
// ...
})
// ...
@@ -322,6 +329,37 @@ public/
+ sprite-bar.87654def.svg
```
+With the following metadata in `src/shared/ui/icon/sprite.gen.ts`:
+
+```typescript
+export interface SpritesMap {
+ 'sprite-foo': 'first' | 'second';
+ 'sprite-bar': ' /* ... */ ';
+}
+export const SPRITES_META = {
+ 'sprite-foo': {
+ filePath: 'sprite-foo.12abc678.svg',
+ items: {
+ first: {
+ // all items will have `viewBox`, `width` and `height` properties
+ viewBox: '0 0 48 48',
+ width: 48,
+ height: 48
+ },
+ second: {
+ /* ... */
+ }
+ }
+ },
+ 'sprite-bar': {
+ filePath: 'sprite-bar.87654def.svg',
+ items: {
+ /* ... */
+ }
+ }
+};
+```
+
And updates of `Icon` component will be like this:
> This example is based on implementation from [Building Icon component with TailwindCSS](#building-icon-component-with-tailwindcss-see-example) recipe and our [Vite application example (link to GH repo)](https://github.com/secundant/neodx/tree/main/examples/svg-vite)
@@ -329,17 +367,17 @@ And updates of `Icon` component will be like this:
```diff
+ import { SPRITES_META, type SpritesMap } from './sprite.gen';
-+ export function Icon({ name, viewBox: viewBoxFromProps, /* ... */ }) {
++ export function Icon({ name, /* ... */ }) {
const [spriteName, iconName] = name.split('/');
+ const { filePath, items } = SPRITES_META[spriteName];
+ const { viewBox } = items[iconName];
return (
-+
++
);
}
@@ -386,7 +424,9 @@ export default defineConfig({
root: 'assets',
group: true,
output: 'public',
- definitions: 'src/shared/ui/icon/sprite.gen.ts',
+ metadata: {
+ path: 'src/shared/ui/icon/sprite.gen.ts'
+ },
resetColors: {
replaceUnknown: 'currentColor'
}
@@ -445,11 +485,11 @@ export const SPRITES_META: { [K in keyof SpritesMap]: SpritesMap[K][] } = {
As you can see, we have a map of all sprites and meta information about them.
-Now we can use it in our code - for type checking, autocomplete and other cool stuff.
+Now we can use it in our code - for type checking, autocomplete, and other cool stuff.
### Create your Icon component
-> It's a **simple** implementation, you can see a more real one in the "Recipes" section
+> It's a **simple** implementation, you can see a real one in the "Recipes" section
```tsx
// shared/ui/icon/icon.tsx
@@ -463,7 +503,7 @@ export interface IconProps {
export function Icon({ type, name }: IconProps) {
return (
-
+
);
}
@@ -558,7 +598,7 @@ export function Icon({ name, className, viewBox, ...props }: IconProps) {
{...props}
>
{/* For example, "/common.svg#favourite". Change base path if you don't store sprites under the root. */}
-
+
);
}
@@ -639,6 +679,27 @@ export function SomeFeature() {
}
```
+## Migrations
+
+### Move from `definitions` and `experimentalRuntime` options to `metadata API`
+
+Now [metadata](#-content-based-hashes-and-runtime-metadata-generation) is stable
+and covered under one `metadata` option.
+
+```diff
+svg({
+- definitions: 'src/shared/ui/icon/sprite.gen.ts',
+- experimentalRuntime: true,
++ metadata: {
++ path: 'src/shared/ui/icon/sprite.gen.ts',
++ runtime: {
++ size: true,
++ viewBox: true,
++ }
++ }
+});
+```
+
## API
### Node.JS API
@@ -696,36 +757,17 @@ interface Options {
*/
optimize?: boolean;
/**
- * Path to generated definitions file
+ * Configures metadata generation
+ * @example "src/sprites/meta.ts"
+ * @example { path: "meta.ts", runtime: false } // will generate only types
+ * @example { path: "meta.ts", types: 'TypeName', runtime: 'InfoName' } // will generate "interface TypeName" types and "const InfoName" runtime metadata
+ * @example { path: "meta.ts", runtime: { size: true, viewBox: true } } // will generate runtime metadata with size and viewBox
*/
- definitions?: string;
+ metadata?: MetadataPluginParams;
/**
* Reset colors config
*/
resetColors?: ResetColorsPluginParams;
- /**
- * WILL BE CHANGED IN FUTURE
- * Replaces current approach (just array of IDs per sprite) with extended runtime metadata
- *
- * @unstable
- * @example
- * export const SPRITES_META = {
- * 'common-arrows': {
- * fileName: 'common/arrows.a766b3.svg',
- * items: {
- * left: {
- * viewBox: '0 0 24 24',
- * },
- * right: {
- * viewBox: '0 0 24 24',
- * },
- * // ...
- * }
- * },
- * // ...
- * };
- */
- experimentalRuntime?: boolean;
}
```
diff --git a/libs/svg/examples/colors-advanced/generated/sprite-info.ts b/libs/svg/examples/colors-advanced/generated/sprite-info.ts
index 6a02592..725b78e 100644
--- a/libs/svg/examples/colors-advanced/generated/sprite-info.ts
+++ b/libs/svg/examples/colors-advanced/generated/sprite-info.ts
@@ -3,9 +3,12 @@ export interface SpritesMap {
flags: 'ac' | 'ad' | 'ae' | 'af';
logos: 'linkedin' | 'twitter';
}
-
-export const SPRITES_META: { [K in keyof SpritesMap]: SpritesMap[K][] } = {
+export const SPRITES_META = {
sprite: ['close', 'exit', 'favourite', 'folder-colored', 'sort-by-visibility'],
flags: ['ac', 'ad', 'ae', 'af'],
logos: ['linkedin', 'twitter']
+} satisfies {
+ sprite: Array<'close' | 'exit' | 'favourite' | 'folder-colored' | 'sort-by-visibility'>;
+ flags: Array<'ac' | 'ad' | 'ae' | 'af'>;
+ logos: Array<'linkedin' | 'twitter'>;
};
diff --git a/libs/svg/examples/colors/generated/sprite-info.ts b/libs/svg/examples/colors/generated/sprite-info.ts
index 92ddc92..ac8d7d1 100644
--- a/libs/svg/examples/colors/generated/sprite-info.ts
+++ b/libs/svg/examples/colors/generated/sprite-info.ts
@@ -1,7 +1,8 @@
export interface SpritesMap {
sprite: 'custom' | 'fill' | 'mixed' | 'stroke';
}
-
-export const SPRITES_META: { [K in keyof SpritesMap]: SpritesMap[K][] } = {
+export const SPRITES_META = {
sprite: ['custom', 'fill', 'mixed', 'stroke']
+} satisfies {
+ sprite: Array<'custom' | 'fill' | 'mixed' | 'stroke'>;
};
diff --git a/libs/svg/examples/groups-with-root/generated/sprite-info.ts b/libs/svg/examples/groups-with-root/generated/sprite-info.ts
index 1d07f76..cc530dd 100644
--- a/libs/svg/examples/groups-with-root/generated/sprite-info.ts
+++ b/libs/svg/examples/groups-with-root/generated/sprite-info.ts
@@ -2,8 +2,10 @@ export interface SpritesMap {
common: 'close' | 'favourite';
format: 'align-left' | 'tag';
}
-
-export const SPRITES_META: { [K in keyof SpritesMap]: SpritesMap[K][] } = {
+export const SPRITES_META = {
common: ['close', 'favourite'],
format: ['align-left', 'tag']
+} satisfies {
+ common: Array<'close' | 'favourite'>;
+ format: Array<'align-left' | 'tag'>;
};
diff --git a/libs/svg/examples/groups-without-root/generated/sprite-info.ts b/libs/svg/examples/groups-without-root/generated/sprite-info.ts
index 645c88a..dab91a7 100644
--- a/libs/svg/examples/groups-without-root/generated/sprite-info.ts
+++ b/libs/svg/examples/groups-without-root/generated/sprite-info.ts
@@ -2,8 +2,10 @@ export interface SpritesMap {
'assets/common': 'close' | 'favourite';
'assets/format': 'align-left' | 'tag';
}
-
-export const SPRITES_META: { [K in keyof SpritesMap]: SpritesMap[K][] } = {
+export const SPRITES_META = {
'assets/common': ['close', 'favourite'],
'assets/format': ['align-left', 'tag']
+} satisfies {
+ 'assets/common': Array<'close' | 'favourite'>;
+ 'assets/format': Array<'align-left' | 'tag'>;
};
diff --git a/libs/svg/examples/experimental-runtime/assets/common/close.svg b/libs/svg/examples/metadata/assets/common/close.svg
similarity index 100%
rename from libs/svg/examples/experimental-runtime/assets/common/close.svg
rename to libs/svg/examples/metadata/assets/common/close.svg
diff --git a/libs/svg/examples/experimental-runtime/assets/common/favourite.svg b/libs/svg/examples/metadata/assets/common/favourite.svg
similarity index 100%
rename from libs/svg/examples/experimental-runtime/assets/common/favourite.svg
rename to libs/svg/examples/metadata/assets/common/favourite.svg
diff --git a/libs/svg/examples/experimental-runtime/assets/format/align-left.svg b/libs/svg/examples/metadata/assets/format/align-left.svg
similarity index 100%
rename from libs/svg/examples/experimental-runtime/assets/format/align-left.svg
rename to libs/svg/examples/metadata/assets/format/align-left.svg
diff --git a/libs/svg/examples/experimental-runtime/assets/format/tag.svg b/libs/svg/examples/metadata/assets/format/tag.svg
similarity index 100%
rename from libs/svg/examples/experimental-runtime/assets/format/tag.svg
rename to libs/svg/examples/metadata/assets/format/tag.svg
diff --git a/libs/svg/examples/experimental-runtime/generated/common.2eb4b56f.svg b/libs/svg/examples/metadata/generated/common.2eb4b56f.svg
similarity index 100%
rename from libs/svg/examples/experimental-runtime/generated/common.2eb4b56f.svg
rename to libs/svg/examples/metadata/generated/common.2eb4b56f.svg
diff --git a/libs/svg/examples/experimental-runtime/generated/format.c890959d.svg b/libs/svg/examples/metadata/generated/format.c890959d.svg
similarity index 100%
rename from libs/svg/examples/experimental-runtime/generated/format.c890959d.svg
rename to libs/svg/examples/metadata/generated/format.c890959d.svg
diff --git a/libs/svg/examples/experimental-runtime/generated/sprite-info.ts b/libs/svg/examples/metadata/generated/meta.ts
similarity index 50%
rename from libs/svg/examples/experimental-runtime/generated/sprite-info.ts
rename to libs/svg/examples/metadata/generated/meta.ts
index 46cbc3d..841f35d 100644
--- a/libs/svg/examples/experimental-runtime/generated/sprite-info.ts
+++ b/libs/svg/examples/metadata/generated/meta.ts
@@ -2,16 +2,19 @@ export interface SpritesMap {
common: 'close' | 'favourite';
format: 'align-left' | 'tag';
}
-
export const SPRITES_META = {
common: {
filePath: 'common.2eb4b56f.svg',
items: {
close: {
- viewBox: '0 0 48 48'
+ viewBox: '0 0 48 48',
+ width: 48,
+ height: 48
},
favourite: {
- viewBox: '0 0 48 48'
+ viewBox: '0 0 48 48',
+ width: 48,
+ height: 48
}
}
},
@@ -19,20 +22,28 @@ export const SPRITES_META = {
filePath: 'format.c890959d.svg',
items: {
'align-left': {
- viewBox: '0 0 48 48'
+ viewBox: '0 0 48 48',
+ width: 48,
+ height: 48
},
tag: {
- viewBox: '0 0 48 48'
+ viewBox: '0 0 48 48',
+ width: 48,
+ height: 48
}
}
}
-} satisfies {
- [SpriteName in keyof SpritesMap]: {
+} satisfies Record<
+ string,
+ {
filePath: string;
- items: {
- [ItemName in SpritesMap[SpriteName]]: {
+ items: Record<
+ string,
+ {
viewBox: string;
- };
- };
- };
-};
+ width: number;
+ height: number;
+ }
+ >;
+ }
+>;
diff --git a/libs/svg/examples/react/generated/sprite-info.ts b/libs/svg/examples/react/generated/sprite-info.ts
index 1d07f76..cc530dd 100644
--- a/libs/svg/examples/react/generated/sprite-info.ts
+++ b/libs/svg/examples/react/generated/sprite-info.ts
@@ -2,8 +2,10 @@ export interface SpritesMap {
common: 'close' | 'favourite';
format: 'align-left' | 'tag';
}
-
-export const SPRITES_META: { [K in keyof SpritesMap]: SpritesMap[K][] } = {
+export const SPRITES_META = {
common: ['close', 'favourite'],
format: ['align-left', 'tag']
+} satisfies {
+ common: Array<'close' | 'favourite'>;
+ format: Array<'align-left' | 'tag'>;
};
diff --git a/libs/svg/examples/react/icon.tsx b/libs/svg/examples/react/icon.tsx
index cf57d17..26df2f3 100644
--- a/libs/svg/examples/react/icon.tsx
+++ b/libs/svg/examples/react/icon.tsx
@@ -24,7 +24,7 @@ export function Icon({ name, className, viewBox, ...props }: IconProps) {
aria-hidden
{...props}
>
-
+
);
}
diff --git a/libs/svg/examples/simple/generated/sprite-info.ts b/libs/svg/examples/simple/generated/sprite-info.ts
index 8d52b80..2a05ea8 100644
--- a/libs/svg/examples/simple/generated/sprite-info.ts
+++ b/libs/svg/examples/simple/generated/sprite-info.ts
@@ -1,7 +1,8 @@
export interface SpritesMap {
sprite: 'arrow-drop-down' | 'arrow-drop-up';
}
-
-export const SPRITES_META: { [K in keyof SpritesMap]: SpritesMap[K][] } = {
+export const SPRITES_META = {
sprite: ['arrow-drop-down', 'arrow-drop-up']
+} satisfies {
+ sprite: Array<'arrow-drop-down' | 'arrow-drop-up'>;
};
diff --git a/libs/svg/src/__tests__/__snapshots__/examples.test.ts.snap b/libs/svg/src/__tests__/__snapshots__/examples.test.ts.snap
index efc7ae2..71d4b5f 100644
--- a/libs/svg/src/__tests__/__snapshots__/examples.test.ts.snap
+++ b/libs/svg/src/__tests__/__snapshots__/examples.test.ts.snap
@@ -19,7 +19,7 @@ exports[`examples > "colors" example should replay same output 1`] = `
],
[
"generated/sprite-info.ts",
- "4793076202450a65b7b04efe27b0b74ddda29399e95da47f55cfe47450854d12",
+ "68ee54e6a56a9c10472f3613d7b688b49087d69ca2ad2591318a01138ff5f669",
],
]
`;
@@ -60,94 +60,94 @@ exports[`examples > "colors-advanced" example should replay same output 1`] = `
],
[
"generated/sprite-info.ts",
- "ccea76ebfa7898989ef6da5c21ff18e8225a629e84098600b52debde57b37d44",
+ "666e8fa9a10d2e5127d31cb8f8604ab36c16aaba9485ceb31aeb18e87e5cddd0",
],
]
`;
-exports[`examples > "experimental-runtime" example should generate files 1`] = `
+exports[`examples > "groups-with-root" example should generate files 1`] = `
[
"assets/common/close.svg",
"assets/common/favourite.svg",
"assets/format/align-left.svg",
"assets/format/tag.svg",
- "generated/common.2eb4b56f.svg",
- "generated/format.c890959d.svg",
+ "generated/common.svg",
+ "generated/format.svg",
"generated/sprite-info.ts",
]
`;
-exports[`examples > "experimental-runtime" example should replay same output 1`] = `
+exports[`examples > "groups-with-root" example should replay same output 1`] = `
[
[
- "generated/common.2eb4b56f.svg",
+ "generated/common.svg",
"2eb4b56ff266279ca9c0af3b16f7f3114ae35c4555657bb106acbd15ff9d4f52",
],
[
- "generated/format.c890959d.svg",
+ "generated/format.svg",
"c890959d268bf53d6006012438788b4104189d3cfaf997b0b43b2fd4f1a41159",
],
[
"generated/sprite-info.ts",
- "046df9f47812fc3681fd787ae23e64db1f089a509fd18607a57d2613bf4cdff7",
+ "0c82bd5eb1a18ea3337261408254d09db5cee2c8eeb4561ed6bca996b0e0d9c9",
],
]
`;
-exports[`examples > "groups-with-root" example should generate files 1`] = `
+exports[`examples > "groups-without-root" example should generate files 1`] = `
[
"assets/common/close.svg",
"assets/common/favourite.svg",
"assets/format/align-left.svg",
"assets/format/tag.svg",
- "generated/common.svg",
- "generated/format.svg",
+ "generated/assets/common.svg",
+ "generated/assets/format.svg",
"generated/sprite-info.ts",
]
`;
-exports[`examples > "groups-with-root" example should replay same output 1`] = `
+exports[`examples > "groups-without-root" example should replay same output 1`] = `
[
[
- "generated/common.svg",
+ "generated/assets/common.svg",
"2eb4b56ff266279ca9c0af3b16f7f3114ae35c4555657bb106acbd15ff9d4f52",
],
[
- "generated/format.svg",
+ "generated/assets/format.svg",
"c890959d268bf53d6006012438788b4104189d3cfaf997b0b43b2fd4f1a41159",
],
[
"generated/sprite-info.ts",
- "fac807c42c630ce77d6e1fea77b808338cf68776235c48736f3b82aa09e761df",
+ "6280072f9bbc11d77c987a1ce220c9a70197c7612119b86606025455d3feae97",
],
]
`;
-exports[`examples > "groups-without-root" example should generate files 1`] = `
+exports[`examples > "metadata" example should generate files 1`] = `
[
"assets/common/close.svg",
"assets/common/favourite.svg",
"assets/format/align-left.svg",
"assets/format/tag.svg",
- "generated/assets/common.svg",
- "generated/assets/format.svg",
- "generated/sprite-info.ts",
+ "generated/common.2eb4b56f.svg",
+ "generated/format.c890959d.svg",
+ "generated/meta.ts",
]
`;
-exports[`examples > "groups-without-root" example should replay same output 1`] = `
+exports[`examples > "metadata" example should replay same output 1`] = `
[
[
- "generated/assets/common.svg",
+ "generated/common.2eb4b56f.svg",
"2eb4b56ff266279ca9c0af3b16f7f3114ae35c4555657bb106acbd15ff9d4f52",
],
[
- "generated/assets/format.svg",
+ "generated/format.c890959d.svg",
"c890959d268bf53d6006012438788b4104189d3cfaf997b0b43b2fd4f1a41159",
],
[
- "generated/sprite-info.ts",
- "1153d305b1db1f1605d0d5e9b30a19ec4fea65b221553ef862158f31c2270de8",
+ "generated/meta.ts",
+ "789b7ca4856d0b2d1b105e8267a5950f77d4928ef1710b4db05438cefd96c27f",
],
]
`;
@@ -178,7 +178,7 @@ exports[`examples > "react" example should replay same output 1`] = `
],
[
"generated/sprite-info.ts",
- "fac807c42c630ce77d6e1fea77b808338cf68776235c48736f3b82aa09e761df",
+ "0c82bd5eb1a18ea3337261408254d09db5cee2c8eeb4561ed6bca996b0e0d9c9",
],
]
`;
@@ -200,7 +200,7 @@ exports[`examples > "simple" example should replay same output 1`] = `
],
[
"generated/sprite-info.ts",
- "89e0e5e36909c3dce4af5977af027d8779b20860232d34612d27a7778bdcc1e3",
+ "528cef17918096f4bec20bf6e16ff1d970c3999eb5b405469661a12b24c7cac4",
],
]
`;
diff --git a/libs/svg/src/__tests__/__snapshots__/plugins.test.ts.snap b/libs/svg/src/__tests__/__snapshots__/plugins.test.ts.snap
new file mode 100644
index 0000000..2fcf5fd
--- /dev/null
+++ b/libs/svg/src/__tests__/__snapshots__/plugins.test.ts.snap
@@ -0,0 +1,219 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`plugins system > "metadata" plugin > should generate basic runtime info with metadata.runtime as constant name 1`] = `
+"export interface SpritesMap {
+ 'a': 'a' | 'b',
+'b': 'a' | 'b'
+ }
+export const RuntimeExample = {
+ 'a': ['a',
+'b'],
+'b': ['a',
+'b']
+ } satisfies {
+ 'a': Array<'a' | 'b'>,
+'b': Array<'a' | 'b'>
+ };"
+`;
+
+exports[`plugins system > "metadata" plugin > should generate runtime with size 1`] = `
+"export interface SpritesMap {
+ 'a': 'a' | 'b',
+'b': 'a' | 'b'
+ }
+export const SPRITES_META = {
+ 'a': {
+ filePath: 'a.svg',
+ items: {
+ 'a': {
+
+ width: 16, height: 16,
+ },
+'b': {
+
+ width: 16, height: 16,
+ }
+ }
+},
+'b': {
+ filePath: 'b.svg',
+ items: {
+ 'a': {
+
+ width: 16, height: 16,
+ },
+'b': {
+
+ width: 16, height: 16,
+ }
+ }
+}
+ } satisfies Record
+ }>;"
+`;
+
+exports[`plugins system > "metadata" plugin > should generate runtime with size, viewBox and custom name 1`] = `
+"export interface SpritesMap {
+ 'a': 'a' | 'b',
+'b': 'a' | 'b'
+ }
+export const RuntimeExample = {
+ 'a': {
+ filePath: 'a.svg',
+ items: {
+ 'a': {
+ viewBox: '0 0 16 16',
+ width: 16, height: 16,
+ },
+'b': {
+ viewBox: '0 0 16 16',
+ width: 16, height: 16,
+ }
+ }
+},
+'b': {
+ filePath: 'b.svg',
+ items: {
+ 'a': {
+ viewBox: '0 0 16 16',
+ width: 16, height: 16,
+ },
+'b': {
+ viewBox: '0 0 16 16',
+ width: 16, height: 16,
+ }
+ }
+}
+ } satisfies Record
+ }>;"
+`;
+
+exports[`plugins system > "metadata" plugin > should generate runtime with viewBox 1`] = `
+"export interface SpritesMap {
+ 'a': 'a' | 'b',
+'b': 'a' | 'b'
+ }
+export const SPRITES_META = {
+ 'a': {
+ filePath: 'a.svg',
+ items: {
+ 'a': {
+ viewBox: '0 0 16 16',
+
+ },
+'b': {
+ viewBox: '0 0 16 16',
+
+ }
+ }
+},
+'b': {
+ filePath: 'b.svg',
+ items: {
+ 'a': {
+ viewBox: '0 0 16 16',
+
+ },
+'b': {
+ viewBox: '0 0 16 16',
+
+ }
+ }
+}
+ } satisfies Record
+ }>;"
+`;
+
+exports[`plugins system > "metadata" plugin > should generate runtime without types 1`] = `
+"export const RuntimeExample = {
+ 'a': {
+ filePath: 'a.svg',
+ items: {
+ 'a': {
+ viewBox: '0 0 16 16',
+ width: 16, height: 16,
+ },
+'b': {
+ viewBox: '0 0 16 16',
+ width: 16, height: 16,
+ }
+ }
+},
+'b': {
+ filePath: 'b.svg',
+ items: {
+ 'a': {
+ viewBox: '0 0 16 16',
+ width: 16, height: 16,
+ },
+'b': {
+ viewBox: '0 0 16 16',
+ width: 16, height: 16,
+ }
+ }
+}
+ };"
+`;
+
+exports[`plugins system > "metadata" plugin > should generate same default output with metadata as string and as { path } 1`] = `
+"export interface SpritesMap {
+ 'a': 'a' | 'b',
+'b': 'a' | 'b'
+ }
+export const SPRITES_META = {
+ 'a': ['a',
+'b'],
+'b': ['a',
+'b']
+ } satisfies {
+ 'a': Array<'a' | 'b'>,
+'b': Array<'a' | 'b'>
+ };"
+`;
+
+exports[`plugins system > "metadata" plugin > should generate types and runtime at the same time 1`] = `
+"export interface TypesExample {
+ 'a': 'a' | 'b',
+'b': 'a' | 'b'
+ }
+export const RuntimeExample = {
+ 'a': ['a',
+'b'],
+'b': ['a',
+'b']
+ } satisfies {
+ 'a': Array<'a' | 'b'>,
+'b': Array<'a' | 'b'>
+ };"
+`;
+
+exports[`plugins system > "metadata" plugin > should generate types with metadata.types as interface name 1`] = `
+"export interface TypesExample {
+ 'a': 'a' | 'b',
+'b': 'a' | 'b'
+ }
+export const SPRITES_META = {
+ 'a': ['a',
+'b'],
+'b': ['a',
+'b']
+ } satisfies {
+ 'a': Array<'a' | 'b'>,
+'b': Array<'a' | 'b'>
+ };"
+`;
diff --git a/libs/svg/src/__tests__/examples.test.ts b/libs/svg/src/__tests__/examples.test.ts
index 520b0e2..625a269 100644
--- a/libs/svg/src/__tests__/examples.test.ts
+++ b/libs/svg/src/__tests__/examples.test.ts
@@ -52,11 +52,18 @@ describe('examples', () => {
}
]
},
- 'experimental-runtime': {
+ metadata: {
root: 'assets',
group: true,
fileName: '{name}.{hash:8}.svg',
- experimentalRuntime: true
+ metadata: {
+ path: 'generated/meta.ts',
+ types: true,
+ runtime: {
+ viewBox: true,
+ size: true
+ }
+ }
}
};
diff --git a/libs/svg/src/__tests__/plugins.test.ts b/libs/svg/src/__tests__/plugins.test.ts
index a2acd8a..d1df637 100644
--- a/libs/svg/src/__tests__/plugins.test.ts
+++ b/libs/svg/src/__tests__/plugins.test.ts
@@ -2,18 +2,46 @@ import { toArray } from '@neodx/std';
import { createTmpVfs } from '@neodx/vfs/testing-utils';
import { describe, expect, test } from 'vitest';
import type { SvgFile, SvgNode } from '..';
+import { buildSprites } from '..';
import { groupSprites } from '../plugins';
+import type { MetadataPluginParams } from '../plugins/metadata';
import { combinePlugins } from '../plugins/plugin-utils';
describe('plugins system', async () => {
+ const defaultSpritesConfig = {
+ a: ['a/a', 'a/b'],
+ b: ['b/a', 'b/b']
+ };
const emptyContext = { vfs: await createTmpVfs() };
const file = (path: string): SvgFile => ({
name: path,
path: `${path}.svg`,
+ meta: {},
node: {} as SvgNode,
- content: ''
+ content:
+ '\n' +
+ ' \n' +
+ ' '
});
const files = (...paths: Array) => paths.flatMap(toArray).map(file);
+ const sprites = (map: Record) =>
+ new Map(
+ Object.entries(map).map(([name, contents]) => [name, { name, files: files(...contents) }])
+ );
+ const createTestContext = async (
+ spritesConfig: Record = defaultSpritesConfig
+ ) => {
+ const map = sprites(spritesConfig);
+ const vfs = await createTmpVfs({
+ initialFiles: {
+ ...Array.from(map.values())
+ .flatMap(({ files }) => files)
+ .reduce((acc, { path, content }) => ({ ...acc, [path]: content }), {})
+ }
+ });
+
+ return { vfs, map };
+ };
test('resolveEntriesMap should return new map', () => {
const hooks = combinePlugins([
@@ -28,7 +56,7 @@ describe('plugins system', async () => {
]);
});
- test('groupSprites should reorganize sprites', () => {
+ test('"groupSprites" plugin should reorganize sprites', () => {
const original = new Map([['a', { name: 'a', files: files('a/a', 'a/b', 'b/a') }]]);
const hooks = combinePlugins([{ name: 'a' }, groupSprites(), { name: 'b' }]);
@@ -37,4 +65,133 @@ describe('plugins system', async () => {
['b', { name: 'b', files: files('b/a') }]
]);
});
+
+ describe('"metadata" plugin', () => {
+ async function buildWithMetadata(metadata: MetadataPluginParams) {
+ const { vfs } = await createTestContext();
+
+ await buildSprites({
+ vfs,
+ input: ['**/*.svg'],
+ group: true,
+ output: 'public',
+ keepTreeChanges: true,
+ metadata
+ });
+ await vfs.formatChangedFiles();
+
+ return { vfs };
+ }
+
+ test('should disable with metadata: false', async () => {
+ const { vfs } = await buildWithMetadata(false);
+
+ expect(await vfs.exists('metadata.json')).toBe(false);
+ });
+
+ test('should generate same default output with metadata as string and as { path }', async () => {
+ const asString = await buildWithMetadata('runtime.ts');
+ const asPath = await buildWithMetadata({
+ path: 'runtime.ts'
+ });
+
+ expect(await asString.vfs.read('runtime.ts', 'utf-8')).toEqual(
+ await asPath.vfs.read('runtime.ts', 'utf-8')
+ );
+ expect(await asString.vfs.read('runtime.ts', 'utf-8')).toMatchSnapshot();
+ });
+
+ test('should skip output when everything is disabled', async () => {
+ const { vfs } = await buildWithMetadata({
+ path: 'runtime.ts',
+ types: false,
+ runtime: false
+ });
+
+ expect(await vfs.exists('runtime.ts')).toBe(false);
+ });
+
+ test('should generate types with metadata.types as interface name', async () => {
+ const { vfs } = await buildWithMetadata({
+ path: 'runtime.ts',
+ types: 'TypesExample'
+ });
+ const content = await vfs.read('runtime.ts', 'utf-8');
+
+ expect(content).toContain('export interface TypesExample {');
+ expect(content).toMatchSnapshot();
+ });
+
+ test('should generate basic runtime info with metadata.runtime as constant name', async () => {
+ const { vfs } = await buildWithMetadata({
+ path: 'runtime.ts',
+ runtime: 'RuntimeExample'
+ });
+ const content = await vfs.read('runtime.ts', 'utf-8');
+
+ expect(content).toContain('export const RuntimeExample = {');
+ expect(content).toMatchSnapshot();
+ });
+
+ test('should generate types and runtime at the same time', async () => {
+ const { vfs } = await buildWithMetadata({
+ path: 'runtime.ts',
+ types: 'TypesExample',
+ runtime: 'RuntimeExample'
+ });
+ const content = await vfs.read('runtime.ts', 'utf-8');
+
+ expect(content).toContain('export interface TypesExample {');
+ expect(content).toContain('export const RuntimeExample = {');
+ expect(content).toMatchSnapshot();
+ });
+
+ test('should generate runtime with size', async () => {
+ const { vfs } = await buildWithMetadata({
+ path: 'runtime.ts',
+ runtime: {
+ size: true
+ }
+ });
+
+ expect(await vfs.read('runtime.ts', 'utf-8')).toMatchSnapshot();
+ });
+
+ test('should generate runtime with viewBox', async () => {
+ const { vfs } = await buildWithMetadata({
+ path: 'runtime.ts',
+ runtime: {
+ viewBox: true
+ }
+ });
+
+ expect(await vfs.read('runtime.ts', 'utf-8')).toMatchSnapshot();
+ });
+
+ test('should generate runtime with size, viewBox and custom name', async () => {
+ const { vfs } = await buildWithMetadata({
+ path: 'runtime.ts',
+ runtime: {
+ name: 'RuntimeExample',
+ size: true,
+ viewBox: true
+ }
+ });
+
+ expect(await vfs.read('runtime.ts', 'utf-8')).toMatchSnapshot();
+ });
+
+ test('should generate runtime without types', async () => {
+ const { vfs } = await buildWithMetadata({
+ path: 'runtime.mjs',
+ runtime: {
+ name: 'RuntimeExample',
+ size: true,
+ viewBox: true
+ }
+ });
+
+ expect(await vfs.read('runtime.mjs', 'utf-8')).toMatchSnapshot();
+ });
+ });
});
diff --git a/libs/svg/src/__tests__/testing-utils.ts b/libs/svg/src/__tests__/testing-utils.ts
index 1d9c679..60f308e 100644
--- a/libs/svg/src/__tests__/testing-utils.ts
+++ b/libs/svg/src/__tests__/testing-utils.ts
@@ -32,7 +32,7 @@ export async function generateExample(
input: ['**/*.svg'],
output: 'generated',
optimize: true,
- definitions: 'generated/sprite-info.ts',
+ metadata: 'generated/sprite-info.ts',
keepTreeChanges: !write,
...options
});
diff --git a/libs/svg/src/core/create-sprite-builder.ts b/libs/svg/src/core/create-sprite-builder.ts
index c627e0d..1243780 100644
--- a/libs/svg/src/core/create-sprite-builder.ts
+++ b/libs/svg/src/core/create-sprite-builder.ts
@@ -4,11 +4,13 @@ import { compact, isTruthy, quickPluralize } from '@neodx/std';
import type { VFS } from '@neodx/vfs';
import { basename, join } from 'pathe';
import { parse } from 'svgson';
-import type { ResetColorsPluginParams } from '../plugins';
-import { fixViewBox, groupSprites, resetColors, setId, svgo, typescript } from '../plugins';
+import { fixViewBox, groupSprites, legacyTypescript, resetColors, setId, svgo } from '../plugins';
+import { extractSvgMeta } from '../plugins/fix-view-box';
+import { metadata as metadataPlugin, type MetadataPluginParams } from '../plugins/metadata';
import { combinePlugins } from '../plugins/plugin-utils';
+import type { ResetColorsPluginParams } from '../plugins/reset-colors';
import { renderSvgNodesToString } from './render';
-import type { GeneratedSprites, SvgFile, SvgNode } from './types';
+import type { GeneratedSprites, SvgFile, SvgFileMeta, SvgNode } from './types';
export interface CreateSpriteBuilderParams {
vfs: VFS;
@@ -44,8 +46,17 @@ export interface CreateSpriteBuilderParams {
* Should we optimize icons?
*/
optimize?: boolean;
+ /**
+ * Configures metadata generation
+ * @example "src/sprites/meta.ts"
+ * @example { path: "meta.ts", runtime: false } // will generate only types
+ * @example { path: "meta.ts", types: 'TypeName', runtime: 'InfoName' } // will generate "interface TypeName" types and "const InfoName" runtime metadata
+ * @example { path: "meta.ts", runtime: { size: true, viewBox: true } } // will generate runtime metadata with size and viewBox
+ */
+ metadata?: MetadataPluginParams;
/**
* Path to generated definitions file
+ * @deprecated use `metadata` instead
*/
definitions?: string;
/**
@@ -56,6 +67,7 @@ export interface CreateSpriteBuilderParams {
* WILL BE CHANGED IN FUTURE
* Replaces current approach (just array of IDs per sprite) with extended runtime metadata
*
+ * @deprecated use `metadata` instead
* @unstable
* @example
* export const SPRITES_META = {
@@ -85,12 +97,19 @@ export function createSpriteBuilder({
output,
logger,
group: enableGroup,
+ metadata,
fileName: fileNameTemplate = '{name}.svg',
optimize,
definitions,
resetColors: resetColorsParams,
experimentalRuntime
}: CreateSpriteBuilderParams) {
+ if (definitions || experimentalRuntime) {
+ logger?.error(
+ 'DEPRECATED: `definitions` and `experimentalRuntime` options will be removed in future versions, use `metadata` instead'
+ );
+ }
+
const hooks = combinePlugins(
compact([
enableGroup && groupSprites(),
@@ -98,8 +117,10 @@ export function createSpriteBuilder({
fixViewBox(),
resetColors(resetColorsParams),
optimize && svgo(),
- definitions &&
- typescript({
+ !definitions && metadataPlugin(metadata),
+ !metadata &&
+ definitions &&
+ legacyTypescript({
output: definitions,
experimentalRuntime
})
@@ -119,11 +140,15 @@ export function createSpriteBuilder({
return null;
}
try {
- const nodeToFile = (node: SvgNode): SvgFile => ({ name, node, path, content });
+ let meta: SvgFileMeta;
+ const nodeToFile = (node: SvgNode): SvgFile => ({ name, meta, node, path, content });
const node = await parse(await hooks.transformSourceContent(path, content), {
camelcase: true,
- transformNode: node => hooks.transformNode(nodeToFile(node))
+ transformNode: node => {
+ meta = extractSvgMeta(node);
+ return hooks.transformNode(nodeToFile(node));
+ }
});
return nodeToFile(node);
diff --git a/libs/svg/src/core/types.ts b/libs/svg/src/core/types.ts
index ba61846..cfe46e7 100644
--- a/libs/svg/src/core/types.ts
+++ b/libs/svg/src/core/types.ts
@@ -39,12 +39,19 @@ export interface GeneratedSprite extends SpriteGroup {
}
export interface SvgFile {
+ meta: SvgFileMeta;
node: SvgNode;
path: string;
name: string;
content: string;
}
+export interface SvgFileMeta {
+ width?: number;
+ height?: number;
+ viewBox?: string;
+}
+
export interface SvgNode {
name: string;
type: string;
diff --git a/libs/svg/src/plugins/fix-view-box.ts b/libs/svg/src/plugins/fix-view-box.ts
index 0d09524..3f90fec 100644
--- a/libs/svg/src/plugins/fix-view-box.ts
+++ b/libs/svg/src/plugins/fix-view-box.ts
@@ -1,4 +1,5 @@
import { omit } from '@neodx/std';
+import type { SvgFileMeta, SvgNode } from '../core';
import { createPlugin } from './plugin-utils';
/**
@@ -6,17 +7,33 @@ import { createPlugin } from './plugin-utils';
*/
export const fixViewBox = () =>
createPlugin('fix-view-box', {
- transformNode({ node }) {
- const {
- attributes: { viewBox, width, height }
- } = node;
-
+ transformNode({ node, meta: { viewBox } }) {
return {
...node,
attributes: {
...omit(node.attributes, ['width', 'height']),
- viewBox: viewBox || !width || !height ? viewBox : `0 0 ${width} ${height}`
+ viewBox: viewBox as string
}
};
}
});
+
+export function extractSvgMeta({
+ attributes: { width: originalWidth, height: originalHeight, viewBox }
+}: SvgNode): SvgFileMeta {
+ const [parsedWidth, parsedHeight] = viewBox ? parseViewBox(viewBox) : [];
+ const width = originalWidth ? Number.parseFloat(originalWidth) : parsedWidth;
+ const height = originalHeight ? Number.parseFloat(originalHeight) : parsedHeight;
+
+ return {
+ width,
+ height,
+ viewBox: viewBox || (width && height ? `0 0 ${width} ${height}` : undefined)
+ };
+}
+
+const parseViewBox = (viewBox: string): [number, number] => {
+ const [, , width, height] = viewBox.split(' ').map(Number.parseFloat);
+
+ return [width, height];
+};
diff --git a/libs/svg/src/plugins/index.ts b/libs/svg/src/plugins/index.ts
index c6c2b96..228ddca 100644
--- a/libs/svg/src/plugins/index.ts
+++ b/libs/svg/src/plugins/index.ts
@@ -1,5 +1,6 @@
export { fixViewBox } from './fix-view-box';
export { type GroupPluginOptions, groupSprites } from './group';
+export { legacyTypescript, type LegacyTypescriptPluginOptions } from './legacy-typescript';
export type {
AnyColorInput,
ColorPropertyReplacementInput,
@@ -9,4 +10,3 @@ export type {
export { resetColors } from './reset-colors';
export { setId } from './set-id';
export { svgo, type SvgoPluginOptions } from './svgo';
-export { typescript, type TypescriptPluginOptions } from './typescript';
diff --git a/libs/svg/src/plugins/typescript.ts b/libs/svg/src/plugins/legacy-typescript.ts
similarity index 91%
rename from libs/svg/src/plugins/typescript.ts
rename to libs/svg/src/plugins/legacy-typescript.ts
index 2832141..6da1241 100644
--- a/libs/svg/src/plugins/typescript.ts
+++ b/libs/svg/src/plugins/legacy-typescript.ts
@@ -1,19 +1,19 @@
import type { GeneratedSprite, GeneratedSprites, SvgFile } from '../core';
import { createPlugin } from './plugin-utils';
-export interface TypescriptPluginOptions {
+export interface LegacyTypescriptPluginOptions {
output?: string;
metaName?: string;
typeName?: string;
experimentalRuntime?: boolean;
}
-export function typescript({
+export function legacyTypescript({
output = 'sprite.types.ts',
typeName = 'SpritesMap',
metaName = 'SPRITES_META',
experimentalRuntime
-}: TypescriptPluginOptions = {}) {
+}: LegacyTypescriptPluginOptions = {}) {
return createPlugin('typescript', {
async afterWriteAll(entries, context) {
await context.vfs.write(
@@ -80,11 +80,7 @@ const renderSpriteAsExperimentalRuntimeMeta = (sprite: GeneratedSprite) =>
}
}`;
-const renderSvgFileAsExperimentalRuntimeMeta = ({
- node: {
- attributes: { viewBox }
- }
-}: SvgFile) =>
+const renderSvgFileAsExperimentalRuntimeMeta = ({ meta: { viewBox } }: SvgFile) =>
`{
viewBox: '${viewBox}',
}`;
diff --git a/libs/svg/src/plugins/metadata.ts b/libs/svg/src/plugins/metadata.ts
new file mode 100644
index 0000000..157557a
--- /dev/null
+++ b/libs/svg/src/plugins/metadata.ts
@@ -0,0 +1,143 @@
+import type { GeneratedSprite, GeneratedSprites, SvgFile } from '../core';
+import { createPlugin } from './plugin-utils';
+
+export type MetadataPluginParams = false | string | MetadataPluginParamsConfig;
+
+export interface MetadataPluginParamsConfig {
+ path: string;
+ types?: Partial | boolean | string;
+ runtime?: Partial | boolean | string;
+}
+
+export interface MetadataTypesParams {
+ name: string;
+}
+
+export interface MetadataRuntimeParams {
+ name: string;
+ size?: boolean;
+ viewBox?: boolean;
+}
+
+export function metadata(params: MetadataPluginParams = false) {
+ if (!params) return null;
+ const config = Object.assign(
+ {
+ runtime: true,
+ types: true
+ },
+ typeof params === 'string' ? { path: params } : params
+ );
+ const types = toNamedParams({ name: 'SpritesMap' }, config.types);
+ const runtime = toNamedParams({ name: 'SPRITES_META' }, config.runtime);
+ const isTypeScript = config.path.endsWith('.ts');
+
+ if (!types && !runtime) return null;
+ return createPlugin('metadata', {
+ async afterWriteAll(sprites, context) {
+ await context.vfs.write(
+ config.path,
+ [
+ isTypeScript && types && renderTypes(types, sprites),
+ runtime && renderRuntime(runtime, sprites, isTypeScript)
+ ]
+ .filter(Boolean)
+ .join('\n')
+ );
+ }
+ });
+}
+
+const renderTypes = ({ name }: MetadataTypesParams, sprites: GeneratedSprites) =>
+ `export interface ${name} {
+ ${renderIterableAsRecord(
+ sprites.values(),
+ sprite => sprite.name,
+ ({ files }) => files.map(file => stringLiteral(file.name)).join(' | ')
+ )}
+ }`;
+
+const renderRuntime = (
+ params: MetadataRuntimeParams,
+ sprites: GeneratedSprites,
+ isTypeScript: boolean
+) => {
+ const { name, size, viewBox } = params;
+ const detailedRuntime = Boolean(size || viewBox);
+ const satisfies = detailedRuntime
+ ? `Record
+ }>`
+ : `{
+ ${renderIterableAsRecord(
+ sprites.values(),
+ sprite => sprite.name,
+ ({ files }) => `Array<${files.map(file => stringLiteral(file.name)).join(' | ')}>`
+ )}
+ }`;
+
+ return `export const ${name} = {
+ ${renderIterableAsRecord(
+ sprites.values(),
+ sprite => sprite.name,
+ sprite =>
+ detailedRuntime ? renderRuntimeAsDetails(sprite, params) : renderRuntimeAsArray(sprite)
+ )}
+ }${isTypeScript ? ` satisfies ${satisfies}` : ''};`;
+};
+
+const renderRuntimeAsArray = ({ files }: GeneratedSprite) =>
+ `[${files.map(file => stringLiteral(file.name)).join(',\n')}]`;
+
+const renderRuntimeAsDetails = (
+ { files, filePath }: GeneratedSprite,
+ params: MetadataRuntimeParams
+) => `{
+ filePath: '${filePath}',
+ items: {
+ ${renderIterableAsRecord(
+ files,
+ file => file.name,
+ file => renderMetadata(params, file)
+ )}
+ }
+}`;
+
+const renderMetadata = (
+ { size, viewBox: displayViewBox }: MetadataRuntimeParams,
+ { meta: { width, height, viewBox } }: SvgFile
+) =>
+ `{
+ ${displayViewBox ? `viewBox: '${viewBox}',` : ''}
+ ${size ? `width: ${width}, height: ${height},` : ''}
+ }`;
+
+const toNamedParams = (
+ defaultValue: T,
+ params: Partial | boolean | string
+): T | null => {
+ if (!params) return null;
+ if (typeof params === 'string' || typeof params === 'boolean') {
+ return { ...defaultValue, name: params === true ? defaultValue.name : params } as T;
+ }
+ return {
+ ...defaultValue,
+ ...params
+ };
+};
+
+const renderIterableAsRecord = (
+ items: Iterable,
+ key: (item: T) => string,
+ value: (item: T) => string,
+ separator = ',\n'
+) =>
+ Array.from(items)
+ .map(item => `${stringLiteral(key(item))}: ${value(item)}`)
+ .join(separator);
+
+const stringLiteral = (value: string) => `'${value}'`;