| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970 |
- #!/usr/bin/env node
- /**
- * Writes packages/app/lib/emoji/emoji-data.ts for the emoji picker from the
- * unicode-emoji-json package (a devDependency): every base emoji (no skin-tone
- * variants) up to MAX_EMOJI_VERSION that UNA can store as a reaction, with its
- * name, grouped as in Unicode.
- *
- * node scripts/generate-emoji-data.js
- *
- * Newer emoji show as empty boxes on phones that haven't updated their OS, so
- * raise MAX_EMOJI_VERSION only when most devices support that version.
- */
- const fs = require('fs');
- const path = require('path');
- const MAX_EMOJI_VERSION = 15.0;
- const OUT = path.join(__dirname, '..', 'packages', 'app', 'lib', 'emoji', 'emoji-data.ts');
- /**
- * UNA keeps a picked emoji as `e_<hex code points>` in a varchar(32) column and takes one pictograph (with its
- * variation selector, skin tone and ZWJ parts), a keycap or a flag: BxDolVoteReactions::isEmoji and normalizeReaction.
- * Emoji it would refuse (families, couples, England and Scotland flags) stay out of the picker.
- */
- const PART = '(?:\\p{Extended_Pictographic}(?:\\u{FE0F}|[\\u{1F3FB}-\\u{1F3FF}])?)';
- const UNA_EMOJI = new RegExp(`^(?:${PART}(?:\\u{200D}${PART})*|[#*0-9]\\u{FE0F}?\\u{20E3}|\\p{Regional_Indicator}{2})$`, 'u');
- const UNA_CODE_MAX = 32;
- const fitsUna = (emoji) =>
- UNA_EMOJI.test(emoji) && ('e_' + Array.from(emoji, (c) => c.codePointAt(0).toString(16)).join('_')).length <= UNA_CODE_MAX;
- const groups = require('unicode-emoji-json/data-by-group.json');
- const { version } = require('unicode-emoji-json/package.json');
- const out = groups
- .filter((group) => group.slug !== 'component')
- .map((group) => ({
- id: group.slug,
- title: group.name,
- emojis: group.emojis
- .filter((e) => parseFloat(e.emoji_version) <= MAX_EMOJI_VERSION && fitsUna(e.emoji))
- .map((e) => [e.emoji, e.name]),
- }))
- .filter((group) => group.emojis.length > 0);
- const count = out.reduce((n, group) => n + group.emojis.length, 0);
- /** One emoji per line, so a data update reads as a readable diff. */
- function formatGroup(group) {
- const emojis = group.emojis.map((entry) => ` ${JSON.stringify(entry)},`).join('\n');
- return ` {\n id: ${JSON.stringify(group.id)},\n title: ${JSON.stringify(group.title)},\n emojis: [\n${emojis}\n ],\n },`;
- }
- const body = `// Generated by scripts/generate-emoji-data.js from unicode-emoji-json ${version}. Do not edit.
- // ${count} emoji up to Emoji ${MAX_EMOJI_VERSION.toFixed(1)}, without skin-tone variants.
- export type EmojiEntry = readonly [emoji: string, name: string];
- export type EmojiGroup = {
- id: string;
- title: string;
- emojis: readonly EmojiEntry[];
- };
- export const EMOJI_GROUPS: readonly EmojiGroup[] = [
- ${out.map(formatGroup).join('\n')}
- ];
- `;
- fs.mkdirSync(path.dirname(OUT), { recursive: true });
- fs.writeFileSync(OUT, body);
- console.log(`${path.relative(process.cwd(), OUT)}: ${out.length} groups, ${count} emoji, ${Math.round(body.length / 1024)} KB`);
|