import path from "path"; import { SKIP } from "unist-util-visit"; import { processMatches } from "./unified-processors"; import type { Plugin, Compiler } from "unified"; import { Element, Text, type Root } from "hast"; const EMOJI_REGEX = /:[a-zA-Z\d-_]+:/gims; export type CustomEmoji = { id: string; name: string; keywords: string[]; skins: { src: string }[]; native?: undefined; // added by the library; we don't need to set it shortcodes?: string; }; type CustomEmojiCategory = { id: string; name: string; emojis: CustomEmoji[]; }; export type CustomEmojiSet = CustomEmojiCategory[]; type NativeEmoji = { id: string; keywords: string[]; name: string; native: string; shortcodes: string; unified: string; }; export type Emoji = NativeEmoji | CustomEmoji; export const customEmoji: CustomEmoji[] = []; export const cohostPlusCustomEmoji: CustomEmoji[] = []; export const indexableCustomEmoji = new Map( customEmoji.reduce<[string, CustomEmoji][]>((collector, emoji) => { return [...collector, [emoji.name, emoji]]; }, []) ); export const indexableCohostPlusCustomEmoji = new Map( cohostPlusCustomEmoji.reduce<[string, CustomEmoji][]>((collector, emoji) => { return [...collector, [emoji.name, emoji]]; }, []) ); type ParseOptions = { cohostPlus: boolean; }; export const parseEmoji = (options: ParseOptions) => { const compiler: Compiler = processMatches( EMOJI_REGEX, (matches, splits, node, index, parent) => { const els = splits.reduce>( (collector, curr, index) => { const currNode: Text = { type: "text", value: curr, }; const pending = [...collector, currNode]; if (index < matches.length) { const emojiName = matches[index].slice( 1, matches[index].length - 1 ); let emoji = indexableCustomEmoji.get(emojiName); if (!emoji && options.cohostPlus) { emoji = indexableCohostPlusCustomEmoji.get(emojiName); } if (emoji) { pending.push({ type: "element", tagName: "CustomEmoji", properties: { name: emoji.name, url: emoji.skins[0].src, }, children: [], } as Element); } else { pending.push({ type: "text", value: matches[index], }); } } return pending; }, [] as Array ); parent.children.splice(index, 1, ...els); // skip over all the new elements we just created return [SKIP, index + els.length]; } ); return compiler; };