import type { Plugin } from "unified"; import type { Nodes, Root, Element } from "hast"; import { findAndReplace } from "hast-util-find-and-replace"; import { h } from "hastscript"; const RE_EMOJI = /:([\w-]+):/g; interface RehypeCustomEmojiOptions { sourceFile: string; hidpiSourceFile?: string; emojis: Record; size: number; } const DEFAULT_OPTIONS: RehypeCustomEmojiOptions = { sourceFile: "/assets/emojis.png", hidpiSourceFile: undefined, emojis: {}, size: 64, }; const plugin: Plugin<[Partial], Root> = (options) => { const settings: RehypeCustomEmojiOptions = Object.assign( {}, DEFAULT_OPTIONS, options ); const srcset = settings.hidpiSourceFile ? `${settings.sourceFile} 1x, ${settings.hidpiSourceFile} 2x` : undefined; const emojiElement = (x: number, y: number, label: string): Element => { return h("img", { src: settings.sourceFile, srcset, style: `object-fit: none; object-position: -${x}px -${y}px; width: ${settings.size}px; height: ${settings.size}px`, alt: label, title: label, }); }; const replaceEmoji = (_: string, match: string): string | false | Element => { if (!(match in settings.emojis)) return false; const emoji = settings.emojis[match]; return emojiElement(emoji[0], emoji[1], match); }; return (tree: Nodes): void => { findAndReplace(tree, [RE_EMOJI, replaceEmoji]); }; }; export default plugin;