72 lines
2.0 KiB
TypeScript
72 lines
2.0 KiB
TypeScript
import { visit } from 'unist-util-visit'
|
|
import { Plugin, Transformer } from 'unified'
|
|
import { Literal, Parent } from 'unist'
|
|
import type { ElementContent } from 'hast'
|
|
import type { EmojiSet } from 'emoji-mart'
|
|
|
|
import { emojiRegex } from './isStringWithOnlyOneEmoji'
|
|
|
|
export const EMOJI_SET: EmojiSet = 'twitter'
|
|
|
|
const extractText = (
|
|
string: string,
|
|
start: number,
|
|
end: number
|
|
): ElementContent => {
|
|
return {
|
|
type: 'text',
|
|
value: string.slice(start, end)
|
|
}
|
|
}
|
|
|
|
export const emojiPlugin: Plugin<[], Literal<string>> = () => {
|
|
const transformer: Transformer<Literal<string>> = (tree) => {
|
|
visit<Literal<string>, string>(
|
|
tree,
|
|
'text',
|
|
(node, position, parent: Parent<ElementContent> | null) => {
|
|
if (typeof node.value !== 'string') {
|
|
return
|
|
}
|
|
position = position ?? 0
|
|
const definition: ElementContent[] = []
|
|
let lastIndex = 0
|
|
const match = emojiRegex.exec(node.value)
|
|
if (match != null) {
|
|
const value = match[0]
|
|
if (match.index !== lastIndex) {
|
|
definition.push(extractText(node.value, lastIndex, match.index))
|
|
}
|
|
definition.push({
|
|
type: 'element',
|
|
tagName: 'emoji',
|
|
properties: { value },
|
|
children: []
|
|
})
|
|
lastIndex = match.index + value.length
|
|
if (lastIndex !== node.value.length) {
|
|
definition.push(
|
|
extractText(node.value, lastIndex, node.value.length)
|
|
)
|
|
}
|
|
if (parent != null) {
|
|
const last = parent.children.slice(position + 1)
|
|
parent.children = parent.children.slice(0, position)
|
|
parent.children = parent.children.concat(definition)
|
|
parent.children = parent.children.concat(last)
|
|
}
|
|
}
|
|
}
|
|
)
|
|
}
|
|
return transformer
|
|
}
|
|
|
|
declare global {
|
|
namespace JSX {
|
|
interface IntrinsicElements {
|
|
emoji: { value: string }
|
|
}
|
|
}
|
|
}
|