IOweb/node_modules/astro-icon/components/Icon.astro
2026-07-03 15:07:38 -05:00

137 lines
4.3 KiB
Text

---
// @ts-ignore virtual module
import icons, { config } from "virtual:astro-icon";
// @ts-ignore generated by typegen
import type { Icon } from "virtual:astro-icon";
import { getIconData, iconToSVG } from "@iconify/utils";
import type { HTMLAttributes } from "astro/types";
import { cache } from "./cache.js";
import type { IconifyIconBuildResult } from "@iconify/utils/lib/svg/build.js";
interface Props extends HTMLAttributes<"svg"> {
name: Icon;
"is:inline"?: boolean;
title?: string;
desc?: string;
size?: number | string;
width?: number | string;
height?: number | string;
}
class AstroIconError extends Error {
public hint: string = '';
constructor(message: string) {
super(message);
}
}
const req = Astro.request;
const { name = "", title, desc, "is:inline": inline = false, ...props } = Astro.props;
const map = cache.get(req) ?? new Map();
const i = map.get(name) ?? 0;
map.set(name, i + 1);
cache.set(req, map);
const { include = {} } = config;
const sets = Object.keys(include);
const includeSymbol = !inline && i === 0;
let [setName, iconName] = (name as string).split(":");
if (!setName && iconName) {
const err = new AstroIconError(`Invalid "name" provided!`);
if (import.meta.env.DEV) {
err.hint = `The provided value of "${name}" is invalid.\n\nDid you forget the icon set name? If you were attemping to reference a local icon, use the icon's name directly. (ie. "${iconName}")`;
}
throw err;
}
// No iconName, assume local icon reference
if (!iconName) {
// Assign the provided setName to the iconName
iconName = setName;
setName = "local";
// Check if the local icon set exists
if (!icons[setName]) {
const err = new AstroIconError('Unable to load the "local" icon set!');
if (import.meta.env.DEV) {
err.hint =
'It looks like the "local" set was not loaded.\n\nDid you forget to create the icon directory or to update your config?';
}
throw err;
}
// Check if the icon is missing from the local collection
if (!(iconName in icons[setName].icons)) {
const err = new AstroIconError(`Unable to locate "${name}" icon!`);
if (import.meta.env.DEV) {
err.hint = `The icon named "${iconName}" was not found in your local icon directory.\n\nDid you forget to configure your icon directory or make a typo?`;
}
throw err;
}
}
const collection = icons[setName];
// Iconify collection not configured correctly
if (!collection) {
const err = new AstroIconError(`Unable to locate the "${setName}" icon set!`);
if (import.meta.env.DEV) {
if (sets.includes(setName)) {
err.hint = `It looks like the "${setName}" set was not loaded.\n\nDid you install the "@iconify-json/${setName}" dependency?`;
} else {
err.hint = `It looks like the "${setName}" set is not included in your configuration.\n\nDo you need to add the "${setName}" set?`;
}
}
throw err;
}
const iconData = getIconData(collection, iconName ?? setName);
// Missing icon from the icon collection
if (!iconData) {
const err = new AstroIconError(`Unable to locate "${name}" icon!`);
if (import.meta.env.DEV) {
const [maybeStar] = include[setName];
if (maybeStar === "*" || include[setName].includes(iconName)) {
err.hint = `The "${setName}" set does not include an icon named "${iconName}".\n\nIs this a typo?`;
} else {
err.hint = `The "${setName}" set is not configured to include an icon named "${iconName}".\n\nDo you need to add it to your configuration?`;
}
}
throw err;
}
const id = `ai:${collection.prefix}:${iconName ?? setName}`;
if (props.size) {
props.width = props.size;
props.height = props.size;
delete props.size;
}
const renderData = iconToSVG(iconData);
const normalizedProps = { ...renderData.attributes as Partial<IconifyIconBuildResult['attributes']>, ...props };
const normalizedBody = renderData.body;
const { viewBox } = normalizedProps;
if (includeSymbol) {
delete normalizedProps.viewBox;
}
---
<svg {...normalizedProps} data-icon={name}>
{title && <title>{title}</title>}
{desc && <desc>{desc}</desc>}
{
inline ? (
<Fragment id={id} set:html={normalizedBody} />
) : (
<Fragment>
{includeSymbol && <symbol id={id} viewBox={viewBox} set:html={normalizedBody} />}
<use href={`#${id}`} />
</Fragment>
)
}
</svg>