/
Icon.astro
129 lines (112 loc) · 3.94 KB
/
Icon.astro
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
---
// @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";
interface Props extends HTMLAttributes<"svg"> {
name: Icon;
"is:inline"?: boolean;
title?: string;
size?: number;
width?: number;
height?: number;
}
class AstroIconError extends Error {
public hint: string;
constructor(message: string) {
super(message);
}
}
const req = Astro.request;
const { name = "", title, "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, ...props };
const normalizedBody = renderData.body;
---
<svg {...normalizedProps} data-icon={name}>
{title && <title>{title}</title>}
{
inline ? (
<Fragment id={id} set:html={normalizedBody} />
) : (
<Fragment>
{includeSymbol && <symbol id={id} set:html={normalizedBody} />}
<use xlink:href={`#${id}`} />
</Fragment>
)
}
</svg>