mirror of
https://github.com/mastodon/mastodon.git
synced 2025-09-06 18:01:05 +00:00
Provides legacy fallback for browser that don't support regex flag v (#35659)
This commit is contained in:
parent
cb0b608fa7
commit
28b0e5ee78
|
@ -15,17 +15,6 @@ export const SKIN_TONE_CODES = [
|
||||||
0x1f3ff, // Dark skin tone
|
0x1f3ff, // Dark skin tone
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
// TODO: Test and create fallback for browsers that do not handle the /v flag.
|
|
||||||
export const UNICODE_EMOJI_REGEX = /\p{RGI_Emoji}/v;
|
|
||||||
// See: https://www.unicode.org/reports/tr51/#valid-emoji-tag-sequences
|
|
||||||
export const UNICODE_FLAG_EMOJI_REGEX =
|
|
||||||
/\p{RGI_Emoji_Flag_Sequence}|\p{RGI_Emoji_Tag_Sequence}/v;
|
|
||||||
export const CUSTOM_EMOJI_REGEX = /:([a-z0-9_]+):/i;
|
|
||||||
export const ANY_EMOJI_REGEX = new RegExp(
|
|
||||||
`(${UNICODE_EMOJI_REGEX.source}|${CUSTOM_EMOJI_REGEX.source})`,
|
|
||||||
'gv',
|
|
||||||
);
|
|
||||||
|
|
||||||
// Emoji rendering modes. A mode is what we are using to render emojis, a style is what the user has selected.
|
// Emoji rendering modes. A mode is what we are using to render emojis, a style is what the user has selected.
|
||||||
export const EMOJI_MODE_NATIVE = 'native';
|
export const EMOJI_MODE_NATIVE = 'native';
|
||||||
export const EMOJI_MODE_NATIVE_WITH_FLAGS = 'native-flags';
|
export const EMOJI_MODE_NATIVE_WITH_FLAGS = 'native-flags';
|
||||||
|
|
|
@ -9,7 +9,6 @@ import {
|
||||||
EMOJI_TYPE_UNICODE,
|
EMOJI_TYPE_UNICODE,
|
||||||
EMOJI_TYPE_CUSTOM,
|
EMOJI_TYPE_CUSTOM,
|
||||||
EMOJI_STATE_MISSING,
|
EMOJI_STATE_MISSING,
|
||||||
ANY_EMOJI_REGEX,
|
|
||||||
} from './constants';
|
} from './constants';
|
||||||
import {
|
import {
|
||||||
searchCustomEmojisByShortcodes,
|
searchCustomEmojisByShortcodes,
|
||||||
|
@ -32,7 +31,12 @@ import type {
|
||||||
LocaleOrCustom,
|
LocaleOrCustom,
|
||||||
UnicodeEmojiToken,
|
UnicodeEmojiToken,
|
||||||
} from './types';
|
} from './types';
|
||||||
import { emojiLogger, stringHasAnyEmoji, stringHasUnicodeFlags } from './utils';
|
import {
|
||||||
|
anyEmojiRegex,
|
||||||
|
emojiLogger,
|
||||||
|
stringHasAnyEmoji,
|
||||||
|
stringHasUnicodeFlags,
|
||||||
|
} from './utils';
|
||||||
|
|
||||||
const log = emojiLogger('render');
|
const log = emojiLogger('render');
|
||||||
|
|
||||||
|
@ -207,7 +211,7 @@ export function tokenizeText(text: string): TokenizedText {
|
||||||
|
|
||||||
const tokens = [];
|
const tokens = [];
|
||||||
let lastIndex = 0;
|
let lastIndex = 0;
|
||||||
for (const match of text.matchAll(ANY_EMOJI_REGEX)) {
|
for (const match of text.matchAll(anyEmojiRegex())) {
|
||||||
if (match.index > lastIndex) {
|
if (match.index > lastIndex) {
|
||||||
tokens.push(text.slice(lastIndex, match.index));
|
tokens.push(text.slice(lastIndex, match.index));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,32 @@
|
||||||
import debug from 'debug';
|
import debug from 'debug';
|
||||||
|
|
||||||
import {
|
import { emojiRegexPolyfill } from '@/mastodon/polyfills';
|
||||||
CUSTOM_EMOJI_REGEX,
|
|
||||||
UNICODE_EMOJI_REGEX,
|
|
||||||
UNICODE_FLAG_EMOJI_REGEX,
|
|
||||||
} from './constants';
|
|
||||||
|
|
||||||
export function emojiLogger(segment: string) {
|
export function emojiLogger(segment: string) {
|
||||||
return debug(`emojis:${segment}`);
|
return debug(`emojis:${segment}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function stringHasUnicodeEmoji(input: string): boolean {
|
export function stringHasUnicodeEmoji(input: string): boolean {
|
||||||
return UNICODE_EMOJI_REGEX.test(input);
|
return new RegExp(EMOJI_REGEX, supportedFlags()).test(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function stringHasUnicodeFlags(input: string): boolean {
|
export function stringHasUnicodeFlags(input: string): boolean {
|
||||||
return UNICODE_FLAG_EMOJI_REGEX.test(input);
|
if (supportsRegExpSets()) {
|
||||||
|
return new RegExp(
|
||||||
|
'\\p{RGI_Emoji_Flag_Sequence}|\\p{RGI_Emoji_Tag_Sequence}',
|
||||||
|
'v',
|
||||||
|
).test(input);
|
||||||
|
}
|
||||||
|
return new RegExp(
|
||||||
|
// First range is regional indicator symbols,
|
||||||
|
// Second is a black flag + 0-9|a-z tag chars + cancel tag.
|
||||||
|
// See: https://en.wikipedia.org/wiki/Regional_indicator_symbol
|
||||||
|
'(?:\uD83C[\uDDE6-\uDDFF]){2}|\uD83C\uDFF4(?:\uDB40[\uDC30-\uDC7A])+\uDB40\uDC7F',
|
||||||
|
).test(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Constant as this is supported by all browsers.
|
||||||
|
const CUSTOM_EMOJI_REGEX = /:([a-z0-9_]+):/i;
|
||||||
export function stringHasCustomEmoji(input: string) {
|
export function stringHasCustomEmoji(input: string) {
|
||||||
return CUSTOM_EMOJI_REGEX.test(input);
|
return CUSTOM_EMOJI_REGEX.test(input);
|
||||||
}
|
}
|
||||||
|
@ -25,3 +34,23 @@ export function stringHasCustomEmoji(input: string) {
|
||||||
export function stringHasAnyEmoji(input: string) {
|
export function stringHasAnyEmoji(input: string) {
|
||||||
return stringHasUnicodeEmoji(input) || stringHasCustomEmoji(input);
|
return stringHasUnicodeEmoji(input) || stringHasCustomEmoji(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function anyEmojiRegex() {
|
||||||
|
return new RegExp(
|
||||||
|
`${EMOJI_REGEX}|${CUSTOM_EMOJI_REGEX.source}`,
|
||||||
|
supportedFlags('gi'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function supportsRegExpSets() {
|
||||||
|
return 'unicodeSets' in RegExp.prototype;
|
||||||
|
}
|
||||||
|
|
||||||
|
function supportedFlags(flags = '') {
|
||||||
|
if (supportsRegExpSets()) {
|
||||||
|
return `${flags}v`;
|
||||||
|
}
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EMOJI_REGEX = emojiRegexPolyfill?.source ?? '\\p{RGI_Emoji}';
|
||||||
|
|
|
@ -20,5 +20,16 @@ export function loadPolyfills() {
|
||||||
loadIntlPolyfills(),
|
loadIntlPolyfills(),
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- those properties might not exist in old browsers, even if they are always here in types
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- those properties might not exist in old browsers, even if they are always here in types
|
||||||
needsExtraPolyfills && importExtraPolyfills(),
|
needsExtraPolyfills && importExtraPolyfills(),
|
||||||
|
loadEmojiPolyfills(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In the case of no /v support, rely on the emojibase data.
|
||||||
|
async function loadEmojiPolyfills() {
|
||||||
|
if (!('unicodeSets' in RegExp.prototype)) {
|
||||||
|
emojiRegexPolyfill = (await import('emojibase-regex/emoji')).default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Null unless polyfill is needed.
|
||||||
|
export let emojiRegexPolyfill: RegExp | null = null;
|
||||||
|
|
|
@ -69,6 +69,7 @@
|
||||||
"emoji-mart": "npm:emoji-mart-lazyload@latest",
|
"emoji-mart": "npm:emoji-mart-lazyload@latest",
|
||||||
"emojibase": "^16.0.0",
|
"emojibase": "^16.0.0",
|
||||||
"emojibase-data": "^16.0.3",
|
"emojibase-data": "^16.0.3",
|
||||||
|
"emojibase-regex": "^16.0.0",
|
||||||
"escape-html": "^1.0.3",
|
"escape-html": "^1.0.3",
|
||||||
"fast-glob": "^3.3.3",
|
"fast-glob": "^3.3.3",
|
||||||
"fuzzysort": "^3.0.0",
|
"fuzzysort": "^3.0.0",
|
||||||
|
|
|
@ -2659,6 +2659,7 @@ __metadata:
|
||||||
emoji-mart: "npm:emoji-mart-lazyload@latest"
|
emoji-mart: "npm:emoji-mart-lazyload@latest"
|
||||||
emojibase: "npm:^16.0.0"
|
emojibase: "npm:^16.0.0"
|
||||||
emojibase-data: "npm:^16.0.3"
|
emojibase-data: "npm:^16.0.3"
|
||||||
|
emojibase-regex: "npm:^16.0.0"
|
||||||
escape-html: "npm:^1.0.3"
|
escape-html: "npm:^1.0.3"
|
||||||
eslint: "npm:^9.23.0"
|
eslint: "npm:^9.23.0"
|
||||||
eslint-import-resolver-typescript: "npm:^4.2.5"
|
eslint-import-resolver-typescript: "npm:^4.2.5"
|
||||||
|
@ -6565,6 +6566,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"emojibase-regex@npm:^16.0.0":
|
||||||
|
version: 16.0.0
|
||||||
|
resolution: "emojibase-regex@npm:16.0.0"
|
||||||
|
checksum: 10c0/8ee5ff798e51caa581434b1cb2f9737e50195093c4efa1739df21a50a5496f80517924787d865e8cf7d6a0b4c90dbedc04bdc506dcbcc582e14cdf0bb47af0f0
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"emojibase@npm:^16.0.0":
|
"emojibase@npm:^16.0.0":
|
||||||
version: 16.0.0
|
version: 16.0.0
|
||||||
resolution: "emojibase@npm:16.0.0"
|
resolution: "emojibase@npm:16.0.0"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user