Back to memo

Xのリンクを埋め込みカード表示に対応させました

2 min read
Table of Contents

本ブログにて、X(旧Twitter)のリンクを埋め込みカード表示に対応させました。
これまで、Xのリンクを貼ると一般的なリンクカードとして表示されていましたが、
今回の対応により、以下のようにリッチな埋め込みカードで表示されるようになります。

対応方法

それでは、具体的な対応方法をサクッとご紹介しましょう。

前提

  • Astroプロジェクトであること

コード

まず、Xの埋め込み用HTMLを取得するために、
https://publish.twitter.com/oembed のAPIを利用します。
このAPIにリクエストを送ると、埋め込み用のHTMLがJSON形式で返ってきます。

const fetchXEmbed = async (url: string): Promise<string> => {
  const twitterUrl = url.replace('x.com', 'twitter.com');
  try {
    const res = await fetch(
      `https://publish.twitter.com/oembed?url=${encodeURIComponent(twitterUrl)}&omit_script=false&lang=ja`,
    );
    const data = await res.json();
    return data.html;
  } catch (e) {
    // e.g. oEmbed失敗時は別の処理へフォールバック
    return '';
  }
};

次に、このロジックをremarkのプラグインとして組み込みます。
MarkdownのAST(抽象構文木)を走査し、Xのリンクを検出したら、
上記の fetchXEmbed 関数を呼び出して埋め込み用のHTMLを取得。
そして、そのHTMLでMarkdown ASTをゴリゴリと書き換える、という流れです。

export default function remarkOgpLinkCard() {
  return async (tree: any) => {
    let transformers: any[] = [];
    visit(tree, 'paragraph', (paragraphNode: any, index) => {
      if (paragraphNode.children.length !== 1) {
        return tree;
      }

      if (paragraphNode && paragraphNode.data !== undefined) {
        return tree;
      }

      visit(paragraphNode, 'text', (textNode: any) => {
        const urls = textNode.value.match(
          /https?:\/\/[\w/:%#\$&\?\(\)~\.=\+\-]+/g,
        );

        if (urls === null) {
          return;
        }

        const url = urls[0];
        transformers = transformers.concat(async () => {
          let htmlValue = '';

          // Xの埋め込み用HTMLを取得
          if (url.match(/^https?:\/\/(www\.)?x\.com\//)) {
            htmlValue = await fetchXEmbed(url);

            tree.children[index] = {
              type: 'html',
              value: htmlValue,
            };
          }
          
        });
      });
    });

    await Promise.all(transformers.map((transformer) => transformer()));

    return tree;
  };
}

最後に、この作成したプラグインを astro.config.ts に組み込めば完了です。
これで、ブログ記事内でXのリンクを貼るだけで、
自動的に埋め込みカード表示が実現できます。

import remarkOgpLinkCard from './plugins/remarkOgpLinkCard/index.js';

export default defineConfig({
  // ...
  markdown: {
    remarkPlugins: [
      // ...
      remarkOgpLinkCard,
    ],
  },
});

その他

今回はXのリンクを埋め込みカード表示に対応させましたが、
実はInstagramの埋め込み対応も検討しました。
しかし、Instagramの埋め込み用HTMLを取得するにはアクセストークンが必要でして、
その管理という名の「先送り症候群」が発症してしまい、今回は見送る形となりました。

技術的負債?いえ、これは未来への布石です!
近い将来、Instagramの埋め込みカード表示対応も実現できればと考えていますので、乞うご期待ください。