Skip to content

Commit 8a57da1

Browse files
authored
プロジェクトページにfaviconを追加 (#290)
1 parent 9fceae2 commit 8a57da1

File tree

3 files changed

+33
-1
lines changed

3 files changed

+33
-1
lines changed

bun.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"astro": "^5.7.12",
1515
"astro-icon": "^1.1.5",
1616
"bits-ui": "^1.4.8",
17+
"cheerio": "^1.0.0",
1718
"daisyui": "^5.0.35",
1819
"date-fns": "^4.1.0",
1920
"markdown-to-txt": "^2.0.1",

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"astro": "^5.7.12",
3434
"astro-icon": "^1.1.5",
3535
"bits-ui": "^1.4.8",
36+
"cheerio": "^1.0.0",
3637
"daisyui": "^5.0.35",
3738
"date-fns": "^4.1.0",
3839
"markdown-to-txt": "^2.0.1",

src/pages/projects/[...id].astro

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { getProjects } from "+/query";
88
import { Focus } from "+/schema.ts";
99
import type { GetStaticPaths } from "astro";
1010
import { Icon } from "astro-icon/components";
11+
import { load as cheerioLoad } from "cheerio";
1112
1213
export const getStaticPaths = (async () => {
1314
const projects = await getProjects("all");
@@ -18,6 +19,31 @@ export const getStaticPaths = (async () => {
1819
}) satisfies GetStaticPaths;
1920
const { project } = Astro.props;
2021
const { 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+
const faviconRes = await fetch(new URL(iconHref, project.data.website));
33+
iconSrc = `data:${faviconRes.headers.get("content-type")};base64,${Buffer.from(await faviconRes.arrayBuffer()).toString("base64")}`;
34+
} else {
35+
const faviconRes = await fetch(
36+
`${new URL(project.data.website).origin}/favicon.ico`,
37+
);
38+
if (
39+
faviconRes.ok &&
40+
!faviconRes.headers.get("content-type")?.startsWith("text/")
41+
) {
42+
iconSrc = `data:${faviconRes.headers.get("content-type")};base64,${Buffer.from(await faviconRes.arrayBuffer()).toString("base64")}`;
43+
}
44+
}
45+
}
46+
}
2147
---
2248

2349
<GlobalLayout
@@ -95,7 +121,11 @@ const { Content } = await render(project);
95121
{
96122
project.data.website && project.data.status !== "dead" && (
97123
<ActionButton to={project.data.website} class="my-6">
98-
<Icon name="feather:globe" class="mr-2 inline-block h-6 w-6" />
124+
{iconSrc ? (
125+
<img src={iconSrc} alt="favicon" class="inline-block h-6 w-6" />
126+
) : (
127+
<Icon name="feather:globe" class="inline-block h-6 w-6" />
128+
)}
99129
{project.data.title}
100130
</ActionButton>
101131
)

0 commit comments

Comments
 (0)