GitHubのPermalinkから埋め込みを作る

2025/01/05

はじめに

記事にGitHubのコードを埋め込みたくなったので作りました。
以下のような感じになりました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
  "compilerOptions": {
    "target": "ES2020",
    "useDefineForClassFields": true,
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "skipLibCheck": true,
    "moduleResolution": "bundler",
    "noEmit": true,
    "strict": true,
    "types": ["vite/client", "@cloudflare/workers-types", "@types/bun"],
    "jsx": "react-jsx",
    "jsxImportSource": "hono/jsx"
  }
}

ちなみに、リンクはこのサイトのtsconfig.jsonへのリンクです。

Permalinkからコードを取得する

まずは、Permalinkからコードを取得してみます。

export async function fetchPermalink(permalink: string) {
  const response = await fetch(permalink, {
    headers: {
      Accept: "application/json",
    },
  });
  const { title, payload } = await response.json();
  return {
    title,
    lines: payload.blob.rawLines,
    language: payload.blob.language,
  };
}

Acceptヘッダーにapplication/jsonを指定してみたらJSON形式で取得できました。試してみるもんですね。

今回はタイトルとコードと言語が欲しいので、それだけ取り出しています。

コンポーネントを作る

次は、取得したコードを表示するコンポーネントを作ります。
参考までに、このサイトで使ってるコンポーネントを載せておきます。

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
export default async function GithubEmbed({ permalink }: Props) {
  let { line, lineEnd } = parsePermalink(permalink);
  const { title, language, lines } = await fetchPermalink(permalink);
  if (!line) {
    line = 1;
    lineEnd = lines.length;
  }
  if (!lineEnd) {
    lineEnd = line;
  }
  const selectedLines = lines.slice(line - 1, lineEnd);
  const code = selectedLines.join("\n");
  const lang = languageMap[language] ?? "plaintext";
  const highlighter = await getHighlighter(lang);
  const html = highlighter.codeToHtml(code, { lang, theme });

  return (
    <div class={css({ rounded: "md", overflow: "hidden" })}>
      <div
        class={css({
          display: "grid",
          gap: 2,
          gridTemplateColumns: "auto 1fr",
          alignItems: "center",
          p: 2,
          fontSize: "xs",
          bg: "bg.emphasized",
          fontFamily: "latin",
        })}
      >
        <IconFa6BrandsGithub />
        <a
          class={css({
            wordBreak: "break-word",
          })}
          href={permalink}
          target="_blank"
          rel="noopener noreferrer"
        >
          {title}
        </a>
      </div>
      <div
        class={css({
          "& > pre": {
            p: 4,
            overflowX: "auto",
            lineHeight: "normal",
          },
        })}
        // biome-ignore lint/security/noDangerouslySetInnerHtml:
        dangerouslySetInnerHTML={{ __html: html }}
      />
    </div>
  );
}

parsePermalinkはその名の通り、Permalinkをパースする関数です。
ここらへんはChatGPTにリンクの例を投げて書かせると良いでしょう。

その他この記事に登場しない部分は、リポジトリを見てみてください。
多分オープンにしてるはずなので。

おわりに

どうやって実装するか悩んでいたのですが、Acceptヘッダーに気づけた時点で9割くらい解決していました。

せっかく作ったブログ欄に何を載せるか悩んでいたので、軽めのネタが湧いてきて渡りに船でした。