| interface TwitterEmbed { | |
| url: string; | |
| title: string; | |
| html: string; | |
| width: number | null; | |
| height: number | null; | |
| type: string; | |
| cache_age: string; | |
| provider_name: string; | |
| provider_url: string; | |
| version: string; | |
| } | |
| import type { Root } from "mdast"; | |
| import { visit } from "unist-util-visit"; | |
| export default function remarkTwitterEmbed() { | |
| return async (tree: Root) => { | |
| const transformer: any[] = []; | |
| visit(tree, "paragraph", (node) => { | |
| if (node.children.length !== 1) return; | |
| const paragraphNode = node.children[0]; | |
| if (!paragraphNode) return; | |
| visit(paragraphNode, 'text', (textNode) => { | |
| if (!/^https:\/\/(?:www\.)?(?:x)|(?:twitter)\.com\/[a-z_]+\/status\/\d{1,19}?$/.test(textNode.value)) return; | |
| const url = textNode.value; | |
| transformer.push(async () => { | |
| const embed = await fetchEmbed(url); | |
| const iframeNode = { | |
| type: "html", | |
| value: embed.html, | |
| } | |
| node.children.splice(0, 1, iframeNode as { type: "text", value: string }); | |
| }); | |
| }); | |
| }); | |
| try { | |
| await Promise.all(transformer.map((t) => t())); | |
| } catch (error) { | |
| console.error(`[remark-youtube-embed] Error: ${error}`); | |
| } | |
| } | |
| } | |
| const fetchEmbed = async (url: string): Promise<TwitterEmbed> => { | |
| const endpoint = "https://publish.twitter.com/oembed"; | |
| const query = encodeURIComponent(url); | |
| const resp = await fetch(`${endpoint}?url=${query}&hide_thread=true&align=center&lang=ja&theme=dark`); | |
| return await resp.json(); | |
| } |