@r4ai/remark-embed
Version:
[](https://jsr.io/@r4ai/remark-embed) [](https://codecov.io/gh/r4ai/remark-embed) [ • 3.54 kB
JavaScript
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,
},
});