forked from cliid/rehype-twemojify
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
122 lines (111 loc) · 3.16 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/* eslint-disable no-param-reassign */
/**
* @typedef {import('hast').Root} Root
* @typedef {import('twemoji').ParseObject & { framework?: string; exclude?: Array<string>; params: { [key: string]: string | number; } }} Options
* @typedef {(options: void | Options; ch: string) => string} Converter
* @typedef {(params: { [key: string]: string | number; }) => string} Squasher
*/
import emojiRegex from 'emoji-regex';
import GraphemeSplitter from 'grapheme-splitter';
import twemoji from 'twemoji';
import { map } from 'unist-util-map';
const regex = emojiRegex();
const splitter = new GraphemeSplitter();
const BASE_URL = 'https://twemoji.maxcdn.com/v/latest';
const BASE_SIZE = '72x72';
const BASE_EXT = '.png';
/**
* Squash parameters
*
* @type {Converter}
*/
const squasher = (params) => {
let _params = new URLSearchParams();
for (const key in params) {
_params.append(key, params[key].toString());
}
return _params.toString();
};
/**
* Base converter.
*
* @type {Converter}
*/
const base = (options, ch) =>
`${(options && options.base) ?? BASE_URL}/${
(options && options.folder) ?? (options && options.size) ?? BASE_SIZE
}/${twemoji.convert.toCodePoint(ch)}${(options && options.ext) ?? BASE_EXT}`;
/**
* Convert options to `src` url.
*
* @type {Converter}
*/
const frameworkURL = (options, ch) => {
if (!options || (options && !options.framework)) return base(options, ch);
const framework = options.framework;
switch (framework) {
case 'next':
if (options.params) {
options.params.w = options.params.w || '64';
options.params.q = options.params.q || '30';
} else {
options.params = { w: '64', q: '30' };
}
return `/_next/image?url=${base(options, ch)}&${squasher(options.params)}`;
default:
// your framework isn't supported yet...
return base(options, ch);
}
};
/**
* Plugin to twemoji-fy ordinary emojis in HTML.
*
* @type {import('unified').Plugin<[Options?]|void[], Root>}
*/
export default function rehypeTwemojify(options) {
const exclude = (options && options.exclude) ?? [];
return (tree) =>
map(tree, (node) => {
if (node.type !== 'text' || !regex.test(node.value)) {
return node;
}
let c = [],
s = '';
for (const ch of splitter.splitGraphemes(node.value)) {
// console.log(ch + ': ' + (!ch.match(regex) || exclude.indexOf(ch) !== -1));
if (!ch.match(regex) || exclude.indexOf(ch) !== -1) {
s += ch;
} else {
c.push({
type: 'text',
value: s
});
s = '';
c.push({
type: 'element',
tagName: 'img',
properties: {
className: [(options && options.className) ?? 'emoji'],
draggable: 'false',
alt: ch,
decoding: 'async',
src: frameworkURL(options, ch)
},
children: []
});
}
}
if (s !== '') {
c.push({
type: 'text',
value: s
});
s = '';
}
return {
type: 'element',
tagName: 'span',
children: c
};
});
}