@@ -8,6 +8,7 @@ import { getProjects } from "+/query";
88import { Focus } from " +/schema.ts" ;
99import type { GetStaticPaths } from " astro" ;
1010import { Icon } from " astro-icon/components" ;
11+ import { load as cheerioLoad } from " cheerio" ;
1112
1213export const getStaticPaths = (async () => {
1314 const projects = await getProjects (" all" );
@@ -18,6 +19,30 @@ export const getStaticPaths = (async () => {
1819}) satisfies GetStaticPaths ;
1920const { project } = Astro .props ;
2021const { Content } = await render (project );
22+
23+ let iconSrc: string | undefined = undefined ;
24+ if (project .data .website && project .data .status !== " dead" ) {
25+ const websiteRes = await fetch (project .data .website );
26+ if (websiteRes .ok ) {
27+ const websiteHtml = await websiteRes .text ();
28+ const website = cheerioLoad (websiteHtml );
29+ const iconHref = website (" link[rel='icon'], link[rel='shortcut icon']" )[0 ]
30+ ?.attribs .href ;
31+ if (iconHref ) {
32+ iconSrc = new URL (iconHref , project .data .website ).toString ();
33+ } else {
34+ const faviconRes = await fetch (
35+ ` ${new URL (project .data .website ).origin }/favicon.ico ` ,
36+ );
37+ if (
38+ faviconRes .ok &&
39+ ! faviconRes .headers .get (" content-type" )?.startsWith (" text/" )
40+ ) {
41+ iconSrc = faviconRes .url ;
42+ }
43+ }
44+ }
45+ }
2146---
2247
2348<GlobalLayout
@@ -95,7 +120,11 @@ const { Content } = await render(project);
95120 {
96121 project .data .website && project .data .status !== " dead" && (
97122 <ActionButton to = { project .data .website } class = " my-6" >
98- <Icon name = " feather:globe" class = " mr-2 inline-block h-6 w-6" />
123+ { iconSrc ? (
124+ <img src = { iconSrc } alt = " favicon" class = " inline-block h-6 w-6" />
125+ ) : (
126+ <Icon name = " feather:globe" class = " inline-block h-6 w-6" />
127+ )}
99128 { project .data .title } へ
100129 </ActionButton >
101130 )
0 commit comments