Skip to content

Commit 5131857

Browse files
committed
release astro-iconset@0.0.5
1 parent 0ce318f commit 5131857

11 files changed

Lines changed: 1515 additions & 1217 deletions

File tree

packages/astro-iconset/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# astro-iconset Changelog
22

3+
## 0.0.5
4+
5+
### Patch
6+
7+
- Refactor: icon-resolution logic extracted into a shared `core.ts` — eliminates duplication across all framework components.
8+
- Fix: throws a clear error when neither `name` nor `icon` is provided.
9+
310
## 0.0.4
411

512
### Patch
Lines changed: 9 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,11 @@
1-
/// <reference types="vite/client" />
21
/** @jsxImportSource preact */
32
import type { JSX } from "preact";
4-
import { getIconData, iconToSVG, replaceIDs } from "@iconify/utils";
5-
import type { AstroIconCollectionMap } from "../../typings/integration";
63
import type { AstroIconImport } from "../../typings/astro-icon-import";
7-
84
// @ts-ignore generated by typegen
95
import type { Icon as IconName } from "virtual:astro-iconset";
10-
import collections_mod from "virtual:astro-iconset";
11-
12-
const _collections = ((collections_mod as any).default ?? collections_mod) as AstroIconCollectionMap;
6+
import { resolveIcon } from "../shared/core";
137

14-
type BaseProps = Omit<JSX.SVGAttributes<SVGSVGElement>, "name"> & {
8+
type BaseProps = Omit<JSX.IntrinsicElements["svg"], "name"> & {
159
size?: number | string;
1610
title?: string;
1711
desc?: string;
@@ -21,70 +15,19 @@ type IconProps =
2115
| (BaseProps & { name: IconName; icon?: never })
2216
| (BaseProps & { icon: AstroIconImport; name?: never });
2317

24-
function escapeHtml(s: string): string {
25-
return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
26-
}
27-
2818
export default function Icon(props: IconProps) {
2919
const { name, icon, size, width, height, title, desc, ...rest } = props;
30-
31-
if (icon != null && name != null) {
32-
throw new Error('[astro-iconset] Use either "name" or "icon", not both.');
33-
}
34-
if (import.meta.env.DEV) {
35-
if (size != null && (width != null || height != null)) {
36-
console.warn('[astro-iconset] Use either "size" or "width"/"height", not both. "width"/"height" takes priority.');
37-
}
38-
}
39-
40-
const resolvedWidth = width ?? size;
41-
const resolvedHeight = height ?? size;
42-
43-
let body: string;
44-
let svgAttrs: Record<string, string | undefined>;
45-
46-
if (icon) {
47-
const renderData = iconToSVG(icon);
48-
body = replaceIDs(renderData.body);
49-
svgAttrs = {
50-
...(renderData.attributes as Record<string, string>),
51-
...(resolvedWidth != null ? { width: String(resolvedWidth) } : {}),
52-
...(resolvedHeight != null ? { height: String(resolvedHeight) } : {}),
53-
};
54-
} else {
55-
const iconName = name as string;
56-
const colonIdx = iconName.indexOf(":");
57-
const setName = colonIdx === -1 ? "local" : iconName.slice(0, colonIdx);
58-
const iconKey = colonIdx === -1 ? iconName : iconName.slice(colonIdx + 1);
59-
60-
const collection = _collections[setName];
61-
if (!collection)
62-
throw new Error(`[astro-iconset/preact] Icon set "${setName}" not found. Available: ${Object.keys(_collections).join(", ")}`);
63-
64-
const iconData = getIconData(collection, iconKey);
65-
if (!iconData)
66-
throw new Error(`[astro-iconset/preact] Icon "${iconKey}" not found in set "${setName}".`);
67-
68-
const renderData = iconToSVG(iconData);
69-
body = replaceIDs(renderData.body);
70-
svgAttrs = {
71-
...(renderData.attributes as Record<string, string>),
72-
...(resolvedWidth != null ? { width: String(resolvedWidth) } : {}),
73-
...(resolvedHeight != null ? { height: String(resolvedHeight) } : {}),
74-
};
75-
}
20+
const { attrs, inner, dataIcon } = resolveIcon(
21+
{ name: name as string | undefined, icon, size, width: width as number | string | undefined, height: height as number | string | undefined, title, desc },
22+
"preact"
23+
);
7624

7725
return (
7826
<svg
79-
{...(svgAttrs as JSX.SVGAttributes<SVGSVGElement>)}
27+
{...(attrs as JSX.IntrinsicElements["svg"])}
8028
{...rest}
81-
data-icon={icon ? "astro-iconset:import" : (name as string)}
82-
dangerouslySetInnerHTML={{
83-
__html:
84-
(title ? `<title>${escapeHtml(title)}</title>` : "") +
85-
(desc ? `<desc>${escapeHtml(desc)}</desc>` : "") +
86-
body,
87-
}}
29+
data-icon={dataIcon}
30+
dangerouslySetInnerHTML={{ __html: inner }}
8831
/>
8932
);
9033
}
Lines changed: 8 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,9 @@
11
/** @jsxImportSource react */
22
import type { SVGProps } from "react";
3-
import { getIconData, iconToSVG, replaceIDs } from "@iconify/utils";
4-
import type { AstroIconCollectionMap } from "../../typings/integration";
53
import type { AstroIconImport } from "../../typings/astro-icon-import";
6-
74
// @ts-ignore generated by typegen
85
import type { Icon as IconName } from "virtual:astro-iconset";
9-
import collections_mod from "virtual:astro-iconset";
10-
11-
const _collections = ((collections_mod as any).default ?? collections_mod) as AstroIconCollectionMap;
6+
import { resolveIcon } from "../shared/core";
127

138
type BaseProps = Omit<SVGProps<SVGSVGElement>, "name" | "ref"> & {
149
size?: number | string;
@@ -20,84 +15,19 @@ type IconProps =
2015
| (BaseProps & { name: IconName; icon?: never })
2116
| (BaseProps & { icon: AstroIconImport; name?: never });
2217

23-
function escapeHtml(s: string): string {
24-
return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
25-
}
26-
2718
export default function Icon(props: IconProps) {
2819
const { name, icon, size, width, height, title, desc, ...rest } = props;
29-
30-
if (icon != null && name != null) {
31-
throw new Error('[astro-iconset] Use either "name" or "icon", not both.');
32-
}
33-
if (import.meta.env.DEV) {
34-
if (size != null && (width != null || height != null)) {
35-
console.warn('[astro-iconset] Use either "size" or "width"/"height", not both. "width"/"height" takes priority.');
36-
}
37-
}
38-
39-
const resolvedWidth = width ?? size;
40-
const resolvedHeight = height ?? size;
41-
42-
let body: string;
43-
let svgAttrs: Record<string, string | undefined>;
44-
45-
if (icon) {
46-
const renderData = iconToSVG(icon);
47-
body = replaceIDs(renderData.body);
48-
svgAttrs = {
49-
...(renderData.attributes as Record<string, string>),
50-
...(resolvedWidth != null ? { width: String(resolvedWidth) } : {}),
51-
...(resolvedHeight != null ? { height: String(resolvedHeight) } : {}),
52-
};
53-
} else {
54-
const iconName = name as string;
55-
const colonIdx = iconName.indexOf(":");
56-
let setName: string;
57-
let iconKey: string;
58-
59-
if (colonIdx === -1) {
60-
setName = "local";
61-
iconKey = iconName;
62-
} else {
63-
setName = iconName.slice(0, colonIdx);
64-
iconKey = iconName.slice(colonIdx + 1);
65-
}
66-
67-
const collection = _collections[setName];
68-
if (!collection) {
69-
throw new Error(
70-
`[astro-iconset/react] Icon set "${setName}" not found. Available: ${Object.keys(_collections).join(", ")}`
71-
);
72-
}
73-
74-
const iconData = getIconData(collection, iconKey);
75-
if (!iconData) {
76-
throw new Error(
77-
`[astro-iconset/react] Icon "${iconKey}" not found in set "${setName}".`
78-
);
79-
}
80-
81-
const renderData = iconToSVG(iconData);
82-
body = replaceIDs(renderData.body);
83-
svgAttrs = {
84-
...(renderData.attributes as Record<string, string>),
85-
...(resolvedWidth != null ? { width: String(resolvedWidth) } : {}),
86-
...(resolvedHeight != null ? { height: String(resolvedHeight) } : {}),
87-
};
88-
}
20+
const { attrs, inner, dataIcon } = resolveIcon(
21+
{ name: name as string | undefined, icon, size, width: width as number | string | undefined, height: height as number | string | undefined, title, desc },
22+
"react"
23+
);
8924

9025
return (
9126
<svg
92-
{...(svgAttrs as SVGProps<SVGSVGElement>)}
27+
{...(attrs as SVGProps<SVGSVGElement>)}
9328
{...rest}
94-
data-icon={icon ? "astro-iconset:import" : (name as string)}
95-
dangerouslySetInnerHTML={{
96-
__html:
97-
(title ? `<title>${escapeHtml(title)}</title>` : "") +
98-
(desc ? `<desc>${escapeHtml(desc)}</desc>` : "") +
99-
body,
100-
}}
29+
data-icon={dataIcon}
30+
dangerouslySetInnerHTML={{ __html: inner }}
10131
/>
10232
);
10333
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/// <reference types="vite/client" />
2+
/// <reference path="../../typings/virtual.d.ts" />
3+
import { getIconData, iconToSVG, replaceIDs } from "@iconify/utils";
4+
import type { AstroIconCollectionMap } from "../../typings/integration";
5+
import type { AstroIconImport } from "../../typings/astro-icon-import";
6+
import collections_mod from "virtual:astro-iconset";
7+
8+
const _collections = ((collections_mod as any).default ?? collections_mod) as AstroIconCollectionMap;
9+
10+
export interface ResolveIconInput {
11+
name?: string;
12+
icon?: AstroIconImport;
13+
size?: number | string;
14+
width?: number | string;
15+
height?: number | string;
16+
title?: string;
17+
desc?: string;
18+
}
19+
20+
export interface ResolvedIcon {
21+
attrs: Record<string, string>;
22+
inner: string;
23+
dataIcon: string;
24+
}
25+
26+
function escapeHtml(s: string): string {
27+
return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
28+
}
29+
30+
export function resolveIcon(input: ResolveIconInput, framework: string): ResolvedIcon {
31+
const { name, icon, size, width, height, title, desc } = input;
32+
33+
if (icon != null && name != null) {
34+
throw new Error('[astro-iconset] Use either "name" or "icon", not both.');
35+
}
36+
if (icon == null && name == null) {
37+
throw new Error('[astro-iconset] Either "name" or "icon" must be provided.');
38+
}
39+
if (import.meta.env.DEV) {
40+
if (size != null && (width != null || height != null)) {
41+
console.warn('[astro-iconset] Use either "size" or "width"/"height", not both. "width"/"height" takes priority.');
42+
}
43+
}
44+
45+
const resolvedWidth = width ?? size;
46+
const resolvedHeight = height ?? size;
47+
48+
let body: string;
49+
let attrs: Record<string, string> = {};
50+
51+
if (icon) {
52+
const renderData = iconToSVG(icon);
53+
body = replaceIDs(renderData.body);
54+
attrs = { ...(renderData.attributes as Record<string, string>) };
55+
} else {
56+
const iconName = name as string;
57+
const colonIdx = iconName.indexOf(":");
58+
const setName = colonIdx === -1 ? "local" : iconName.slice(0, colonIdx);
59+
const iconKey = colonIdx === -1 ? iconName : iconName.slice(colonIdx + 1);
60+
61+
const collection = _collections[setName];
62+
if (!collection)
63+
throw new Error(`[astro-iconset/${framework}] Icon set "${setName}" not found. Available: ${Object.keys(_collections).join(", ")}`);
64+
65+
const iconData = getIconData(collection, iconKey);
66+
if (!iconData)
67+
throw new Error(`[astro-iconset/${framework}] Icon "${iconKey}" not found in set "${setName}".`);
68+
69+
const renderData = iconToSVG(iconData);
70+
body = replaceIDs(renderData.body);
71+
attrs = { ...(renderData.attributes as Record<string, string>) };
72+
}
73+
74+
if (resolvedWidth != null) attrs.width = String(resolvedWidth);
75+
if (resolvedHeight != null) attrs.height = String(resolvedHeight);
76+
77+
const inner =
78+
(title ? `<title>${escapeHtml(title)}</title>` : "") +
79+
(desc ? `<desc>${escapeHtml(desc)}</desc>` : "") +
80+
body;
81+
82+
return { attrs, inner, dataIcon: icon ? "astro-iconset:import" : (name as string) };
83+
}

0 commit comments

Comments
 (0)