UNPKG

@r4ai/remark-embed

Version:

[![JSR](https://jsr.io/badges/@r4ai/remark-embed)](https://jsr.io/@r4ai/remark-embed) [![codecov](https://codecov.io/gh/r4ai/remark-embed/graph/badge.svg?token=B9EZXC0PR8)](https://codecov.io/gh/r4ai/remark-embed) [![CI](https://github.com/r4ai/remark-emb

97 lines (96 loc) 3.54 kB
import { defu } from "defu"; import { unfurl } from "unfurl.js"; import { htmlPreset } from "./presets/html.js"; /** * The default options for the {@link transformerLinkCard}. */ export const defaultTransformerLinkCardOptions = htmlPreset(); /** * A transformer to generate link cards. * Generates a link card for a URL using the Open Graph metadata. * * @example * ```ts * const html = ( * await unified() * .use(remarkParse) * .use(remarkRehype) * .use(remarkEmbed, { * transformers: [transformerLinkCard()], * }) * .use(rehypeStringify) * .process("<https://r4ai.dev/posts/docker_tutorial/>") * ).toString() * ``` * Yield: * ```html * <p> * <a href="https://r4ai.dev/posts/docker_tutorial/" class="link-card" target="_blank" rel="noopener noreferrer"> * <div class="link-card__container"> * <div class="link-card__info"> * <div class="link-card__title">Docker 入門 | r4ai.dev</div> * <div class="link-card__description">Tech blog by Rai</div> * <div class="link-card__link"> * <img class="link-card__favicon" src="https://r4ai.dev/favicon.svg" alt="Favicon for r4ai.dev" loading="lazy" decoding="async"> * <span class="link-card__hostname">r4ai.dev</span> * </div> * </div> * <div class="link-card__image"> * <img src="https://r4ai.dev/posts/docker_tutorial/ogimage.png" loading="lazy" decoding="async"> * </div> * </div> * </a> * </p> * ``` */ export const transformerLinkCard = (_options) => { const options = defu(_options, defaultTransformerLinkCardOptions); const cache = new Map(); return { name: "link-card", match: async (url) => { const metadata = cache.get(url.href) ?? (await unfurl(url.href)); if (metadata == null) return false; cache.set(url.href, metadata); return true; }, tagName: async (url) => { const metadata = cache.get(url.href); if (metadata == null) throw new Error("No metadata found for URL"); const tagName = await options.tagName(getUrlInfo(url, metadata)); return tagName; }, properties: async (url) => { const metadata = cache.get(url.href); if (metadata == null) throw new Error("No metadata found for URL"); const properties = await options.properties(getUrlInfo(url, metadata)); return properties; }, children: async (url) => { const metadata = cache.get(url.href); if (metadata == null) throw new Error("No metadata found for URL"); const children = await options.children(getUrlInfo(url, metadata)); return children; }, }; }; const getUrlInfo = (url, metadata) => ({ url: metadata.open_graph?.url ?? url.href, title: metadata.open_graph?.title ?? metadata.title ?? metadata.twitter_card?.title, description: metadata.open_graph?.description ?? metadata.description ?? metadata.twitter_card?.description, favicon: metadata.favicon, image: { src: metadata.open_graph?.images?.at(0)?.url ?? metadata.twitter_card?.images?.at(0)?.url, alt: metadata.open_graph?.images?.at(0)?.alt ?? metadata.twitter_card?.images?.at(0)?.alt, }, });