rehype-custom-emoji-plugin/src/index.ts
2025-03-29 23:04:15 +01:00

55 lines
1.5 KiB
TypeScript

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<string, [number, number]>;
size: number;
}
const DEFAULT_OPTIONS: RehypeCustomEmojiOptions = {
sourceFile: "/assets/emojis.png",
hidpiSourceFile: undefined,
emojis: {},
size: 64,
};
const plugin: Plugin<[Partial<RehypeCustomEmojiOptions>], 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;