70 lines
2.4 KiB
TypeScript
70 lines
2.4 KiB
TypeScript
import emojiShortcodes from 'emojibase-data/en/shortcodes/emojibase.json'
|
|
|
|
export type StandardEmojiShortcodeEntry = {
|
|
emoji: string
|
|
shortcode: string
|
|
}
|
|
|
|
// Skin-tone aliases (`wave_tone3`) are ~40% of the dataset and would drown the suggestion list.
|
|
const SKIN_TONE_SHORTCODE = /_tone\d(?:-\d)?$/
|
|
|
|
const CATALOG = Object.entries(emojiShortcodes).flatMap(([hexcode, value]) => {
|
|
const shortcodes = (typeof value === 'string' ? [value] : value).filter(
|
|
(shortcode) => !SKIN_TONE_SHORTCODE.test(shortcode)
|
|
)
|
|
return shortcodes.length > 0 ? [{ emoji: hexcodeToEmoji(hexcode), shortcodes }] : []
|
|
})
|
|
|
|
export const STANDARD_EMOJI_SHORTCODE_ENTRIES: readonly StandardEmojiShortcodeEntry[] =
|
|
CATALOG.flatMap(({ emoji, shortcodes }) => shortcodes.map((shortcode) => ({ emoji, shortcode })))
|
|
|
|
const PRIMARY_SHORTCODE_BY_EMOJI = new Map(
|
|
CATALOG.map(({ emoji, shortcodes }) => [
|
|
normalizeEmojiLookup(emoji),
|
|
primaryShortcode(shortcodes)
|
|
])
|
|
)
|
|
|
|
/**
|
|
* Pick the alias that reads best as a branch or directory name: skip `+1`/`-1` so the name
|
|
* starts with a letter, then cryptic stubs (👎 `no`, ✌ `v`) and the `flag_xx` namespacing
|
|
* prefix, both of which have a spelled-out alias (`thumbsdown`, `victory`, `germany`).
|
|
*/
|
|
function primaryShortcode(shortcodes: readonly string[]): string {
|
|
const named = shortcodes.filter((candidate) => /^[a-z]/i.test(candidate))
|
|
return (
|
|
named.find((candidate) => candidate.length >= 3 && !candidate.startsWith('flag_')) ??
|
|
named.find((candidate) => candidate.length >= 3) ??
|
|
named[0] ??
|
|
shortcodes[0]
|
|
)
|
|
}
|
|
|
|
const EMOJI_SEGMENTER = new Intl.Segmenter('en', { granularity: 'grapheme' })
|
|
|
|
export function replaceKnownEmojiWithShortcodes(input: string): string {
|
|
return Array.from(EMOJI_SEGMENTER.segment(input), ({ segment }) => {
|
|
const shortcode = PRIMARY_SHORTCODE_BY_EMOJI.get(normalizeEmojiLookup(segment))
|
|
return shortcode ? ` ${shortcode.replaceAll('_', '-')} ` : segment
|
|
}).join('')
|
|
}
|
|
|
|
function normalizeEmojiLookup(emoji: string): string {
|
|
return Array.from(emoji)
|
|
.filter((character) => {
|
|
const codepoint = character.codePointAt(0)
|
|
return (
|
|
character !== '\ufe0f' &&
|
|
(codepoint === undefined || codepoint < 0x1f3fb || codepoint > 0x1f3ff)
|
|
)
|
|
})
|
|
.join('')
|
|
}
|
|
|
|
function hexcodeToEmoji(hexcode: string): string {
|
|
return hexcode
|
|
.split('-')
|
|
.map((codepoint) => String.fromCodePoint(Number.parseInt(codepoint, 16)))
|
|
.join('')
|
|
}
|