Skip to content

Commit 670a5f2

Browse files
authored
fix(message-rewrite): Remove emojis inside links (#800)
1 parent e581072 commit 670a5f2

27 files changed

+158
-94
lines changed

packages/layout/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@
4646
"react-i18next": "~11.15.4"
4747
},
4848
"devDependencies": {
49-
"@rocket.chat/fuselage": "workspace:~",
5049
"@rocket.chat/eslint-config-alt": "workspace:~",
50+
"@rocket.chat/fuselage": "workspace:~",
5151
"@rocket.chat/prettier-config": "workspace:~",
5252
"@storybook/addon-essentials": "~6.4.18",
5353
"@storybook/addons": "~6.4.18",

packages/message-parser/src/grammar.pegjs

+4-5
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
strike,
2828
task,
2929
tasks,
30-
unorderedList,
30+
unorderedList
3131
} = require('./utils');
3232
}
3333

@@ -371,11 +371,11 @@ hexByte = a:hexdigit b:hexdigit { return parseInt(a + b, 16); }
371371

372372
domainName
373373
= "localhost"
374-
/ $(domainNameLabel ("." domainNameLabel)+)
374+
/ $(domainNameLabel ("." domainChar domainNameLabel)+)
375375

376-
domainNameLabel = $(domainChar domainChar+ $("-" domainChar+)*)
376+
domainNameLabel = $(domainChar+ $("-" domainChar+)*)
377377

378-
domainChar = !"/" !"|" !">" !"<" !safe !extra !EndOfLine !Space .
378+
domainChar = !"\\" !"/" !"|" !">" !"<" !safe !extra !EndOfLine !Space .
379379

380380
/**
381381
*
@@ -546,7 +546,6 @@ inlineKatexEnd
546546
/ & { return options.katex?.dollarSyntax; } "$"
547547

548548
/* Emoticons */
549-
550549
emoticon = & { return options.emoticons; } e:emoticonPattern { return e; }
551550

552551
emoticonPattern

packages/message-parser/src/utils.ts

+30-6
Original file line numberDiff line numberDiff line change
@@ -131,21 +131,45 @@ export const emoticon = (emoticon: string, shortCode: string): Emoji => ({
131131
shortCode,
132132
});
133133

134+
const joinEmoji = (
135+
current: Inlines,
136+
previous: Inlines | undefined,
137+
next: Inlines | undefined
138+
): Inlines => {
139+
if (current.type !== 'EMOJI' || !current.value || (!previous && !next)) {
140+
return current;
141+
}
142+
143+
const hasEmojiAsNeighbor =
144+
previous?.type === current.type || current.type === next?.type;
145+
const hasPlainAsNeighbor =
146+
(previous?.type === 'PLAIN_TEXT' && previous.value.trim() !== '') ||
147+
(next?.type === 'PLAIN_TEXT' && next.value.trim() !== '');
148+
149+
if (current.value && (hasEmojiAsNeighbor || hasPlainAsNeighbor)) {
150+
return current.value;
151+
}
152+
153+
return current;
154+
};
155+
134156
export const reducePlainTexts = (
135157
values: Paragraph['value']
136158
): Paragraph['value'] =>
137159
values.reduce((result, item, index) => {
138-
if (index > 0) {
139-
const previous = result[result.length - 1];
140-
if (item.type === 'PLAIN_TEXT' && item.type === previous.type) {
141-
previous.value += item.value;
160+
const next = values[index + 1];
161+
const current = joinEmoji(item, values[index - 1], next);
162+
const previous: Inlines = result[result.length - 1];
163+
164+
if (previous) {
165+
if (current.type === 'PLAIN_TEXT' && current.type === previous.type) {
166+
previous.value += current.value;
142167
return result;
143168
}
144169
}
145170

146-
return [...result, item];
171+
return [...result, current];
147172
}, [] as Paragraph['value']);
148-
149173
export const lineBreak = (): LineBreak => ({
150174
type: 'LINE_BREAK',
151175
value: undefined,

packages/message-parser/tests/any.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { parser } from '../src';
1+
import { parse } from '../src';
22
import { paragraph, plain } from '../src/utils';
33

44
test.each([
@@ -9,5 +9,5 @@ test.each([
99
[paragraph([plain('free text with unxpected/unfinished blocks *bold_')])],
1010
],
1111
])('parses %p', (input, output) => {
12-
expect(parser(input)).toMatchObject(output);
12+
expect(parse(input)).toMatchObject(output);
1313
});

packages/message-parser/tests/blockquotes.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { parser } from '../src';
1+
import { parse } from '../src';
22
import { paragraph, plain, quote, bold } from '../src/utils';
33

44
test.each([
@@ -36,5 +36,5 @@ As Rocket Cat said:
3636
],
3737
],
3838
])('parses %p', (input, output) => {
39-
expect(parser(input)).toMatchObject(output);
39+
expect(parse(input)).toMatchObject(output);
4040
});

packages/message-parser/tests/codeFence.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { parser } from '../src';
1+
import { parse } from '../src';
22
import { paragraph, plain, codeLine, code } from '../src/utils';
33

44
const multiply = <T>(a: number, element: T): Array<T> =>
@@ -79,5 +79,5 @@ code
7979
[code([codeLine(plain(`# code`)), codeLine(plain(`**code**`))])],
8080
],
8181
])('parses %p', (input, output) => {
82-
expect(parser(input)).toMatchObject(output);
82+
expect(parse(input)).toMatchObject(output);
8383
});

packages/message-parser/tests/color.test.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { parser } from '../src';
1+
import { parse } from '../src';
22
import { color, paragraph, plain } from '../src/utils';
33

44
test.each([
@@ -26,9 +26,9 @@ test.each([
2626
['color:#c7', [paragraph([plain('color:#c7')])], undefined],
2727
['color:#zzz', [paragraph([plain('color:#zzz')])], undefined],
2828
])('parses %p', (input, output, disabledOutput) => {
29-
expect(parser(input, { colors: true })).toMatchObject(output);
29+
expect(parse(input, { colors: true })).toMatchObject(output);
3030

3131
if (disabledOutput) {
32-
expect(parser(input, { colors: false })).toMatchObject(disabledOutput);
32+
expect(parse(input, { colors: false })).toMatchObject(disabledOutput);
3333
}
3434
});

packages/message-parser/tests/email.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { parser } from '../src';
1+
import { parse } from '../src';
22
import { link, paragraph, plain } from '../src/utils';
33

44
test.each([
@@ -150,5 +150,5 @@ test.each([
150150
// [paragraph([plain('My email is fake@gmail.comf')])],
151151
// ],
152152
])('parses %p', (input, output) => {
153-
expect(parser(input)).toMatchObject(output);
153+
expect(parse(input)).toMatchObject(output);
154154
});

packages/message-parser/tests/emoji.test.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { parser } from '../src';
1+
import { parse } from '../src';
22
import { emoji, bigEmoji, paragraph, plain, emojiUnicode } from '../src/utils';
33

44
test.each([
@@ -14,12 +14,14 @@ test.each([
1414
[paragraph([plain('asdas '), emoji('smille'), plain(' asd')])],
1515
],
1616
[
17-
'normal emojis :smile::smile::smile:',
17+
'normal emojis :smile: :smile: :smile:',
1818
[
1919
paragraph([
2020
plain('normal emojis '),
2121
emoji('smile'),
22+
plain(' '),
2223
emoji('smile'),
24+
plain(' '),
2325
emoji('smile'),
2426
]),
2527
],
@@ -43,7 +45,7 @@ test.each([
4345
[':smile::smile:', [bigEmoji([emoji('smile'), emoji('smile')])]],
4446
[':smile:', [bigEmoji([emoji('smile')])]],
4547
])('parses %p', (input, output) => {
46-
expect(parser(input)).toMatchObject(output);
48+
expect(parse(input)).toMatchObject(output);
4749
});
4850

4951
test.each([
@@ -80,5 +82,5 @@ test.each([
8082
],
8183
['👆🏺', [bigEmoji([emojiUnicode('👆'), emojiUnicode('🏺')])]],
8284
])('parses %p', (input, output) => {
83-
expect(parser(input)).toMatchObject(output);
85+
expect(parse(input)).toMatchObject(output);
8486
});

packages/message-parser/tests/emoticons.test.ts

+55-14
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,59 @@
1-
import { parser } from '../src';
1+
import { parse } from '../src';
22
import { bigEmoji, paragraph, plain, emoticon } from '../src/utils';
33

44
test.each([
5-
['=) asd', [paragraph([emoticon('=)', 'slight_smile'), plain(' asd')])]],
5+
// Should render normal Emojis
66
[
7-
`=)
8-
=)
9-
`,
7+
`test
8+
:) test`,
109
[
11-
bigEmoji([
12-
emoticon('=)', 'slight_smile'),
13-
emoticon('=)', 'slight_smile'),
10+
paragraph([plain('test')]),
11+
paragraph([
12+
plain(' '),
13+
emoticon(':)', 'slight_smile'),
14+
plain(' test'),
1415
]),
1516
],
1617
],
18+
[':) asd', [paragraph([emoticon(':)', 'slight_smile'), plain(' asd')])]],
19+
[':) asd', [paragraph([emoticon(':)', 'slight_smile'), plain(' asd')])]],
20+
[
21+
' :) asd',
22+
[paragraph([plain(' '), emoticon(':)', 'slight_smile'), plain(' asd')])],
23+
],
24+
['Hi :)', [paragraph([plain('Hi '), emoticon(':)', 'slight_smile')])]],
1725
[
18-
'asdas =) asd',
26+
'asdas :) asd',
1927
[
2028
paragraph([
2129
plain('asdas '),
22-
emoticon('=)', 'slight_smile'),
30+
emoticon(':)', 'slight_smile'),
2331
plain(' asd'),
2432
]),
2533
],
2634
],
2735
[
28-
'normal emojis :):):)',
36+
':) :) :) :)',
2937
[
3038
paragraph([
31-
plain('normal emojis '),
3239
emoticon(':)', 'slight_smile'),
40+
plain(' '),
41+
emoticon(':)', 'slight_smile'),
42+
plain(' '),
43+
emoticon(':)', 'slight_smile'),
44+
plain(' '),
45+
emoticon(':)', 'slight_smile'),
46+
]),
47+
],
48+
],
49+
50+
// Should render BigEmojis
51+
[
52+
`:)
53+
:)
54+
`,
55+
[
56+
bigEmoji([
3357
emoticon(':)', 'slight_smile'),
3458
emoticon(':)', 'slight_smile'),
3559
]),
@@ -45,6 +69,7 @@ test.each([
4569
]),
4670
],
4771
],
72+
4873
[
4974
' :):):) ',
5075
[
@@ -55,6 +80,7 @@ test.each([
5580
]),
5681
],
5782
],
83+
5884
[
5985
'\n :):):) \n',
6086
[
@@ -75,8 +101,9 @@ test.each([
75101
]),
76102
],
77103
],
104+
78105
[
79-
':):)',
106+
':) :)',
80107
[
81108
bigEmoji([
82109
emoticon(':)', 'slight_smile'),
@@ -85,6 +112,20 @@ test.each([
85112
],
86113
],
87114
[':)', [bigEmoji([emoticon(':)', 'slight_smile')])]],
115+
[' :)', [bigEmoji([emoticon(':)', 'slight_smile')])]],
116+
[':) ', [bigEmoji([emoticon(':)', 'slight_smile')])]],
117+
[' :) ', [bigEmoji([emoticon(':)', 'slight_smile')])]],
118+
119+
// Should not render Emojis or BigEmojis if they are not surrounded by spaces
120+
['normal emojis :):):)', [paragraph([plain('normal emojis :):):)')])]],
121+
[':):):) normal emojis', [paragraph([plain(':):):) normal emojis')])]],
122+
[':):):):)', [paragraph([plain(':):):):)')])]],
123+
['10:30', [paragraph([plain('10:30')])]],
124+
['he:)llo', [paragraph([plain('he:)llo')])]],
125+
[':)Hi', [paragraph([plain(':)Hi')])]],
126+
['Hi:) Hi', [paragraph([plain('Hi:) Hi')])]],
127+
['Hi:)', [paragraph([plain('Hi:)')])]],
128+
['@#@#! :)@!@', [paragraph([plain('@#@#! :)@!@')])]],
88129
])('parses %p', (input, output) => {
89-
expect(parser(input, { emoticons: true })).toMatchObject(output);
130+
expect(parse(input, { emoticons: true })).toMatchObject(output);
90131
});

packages/message-parser/tests/emphasis.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { parser } from '../src';
1+
import { parse } from '../src';
22
import {
33
italic,
44
paragraph,
@@ -98,5 +98,5 @@ test.each([
9898
['_hello_text', [paragraph([plain('_hello_text')])]],
9999
['text_hello_', [paragraph([plain('text_hello_')])]],
100100
])('parses %p', (input, output) => {
101-
expect(parser(input)).toMatchObject(output);
101+
expect(parse(input)).toMatchObject(output);
102102
});

packages/message-parser/tests/escaped.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { parser } from '../src';
1+
import { parse } from '../src';
22
import { paragraph, plain, bold } from '../src/utils';
33

44
test.each([
@@ -34,5 +34,5 @@ test.each([
3434
[paragraph([plain('&ouml; not a character entity')])],
3535
],
3636
])('parses %p', (input, output) => {
37-
expect(parser(input)).toMatchObject(output);
37+
expect(parse(input)).toMatchObject(output);
3838
});

packages/message-parser/tests/heading.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { parser } from '../src';
1+
import { parse } from '../src';
22
import {
33
heading,
44
lineBreak,
@@ -54,5 +54,5 @@ test.each([
5454
['# Hello\n', [heading([plain('Hello')], 1), lineBreak()]],
5555
['# # Hello\n', [heading([plain('# Hello')], 1), lineBreak()]],
5656
])('parses %p', (input, output) => {
57-
expect(parser(input)).toMatchObject(output);
57+
expect(parse(input)).toMatchObject(output);
5858
});

packages/message-parser/tests/image.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { parser } from '../src';
1+
import { parse } from '../src';
22
import { image, paragraph, plain } from '../src/utils';
33

44
test.each([
@@ -15,5 +15,5 @@ test.each([
1515
[paragraph([image('https://rocket.chat/assets/img/header/logo.svg')])],
1616
],
1717
])('parses %p', (input, output) => {
18-
expect(parser(input)).toMatchObject(output);
18+
expect(parse(input)).toMatchObject(output);
1919
});
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { parser } from '../src';
1+
import { parse } from '../src';
22
import { inlineCode, paragraph, plain } from '../src/utils';
33

44
test.each([
@@ -8,5 +8,5 @@ test.each([
88
],
99
[`\`code\``, [paragraph([inlineCode(plain('code'))])]],
1010
])('parses %p', (input, output) => {
11-
expect(parser(input)).toMatchObject(output);
11+
expect(parse(input)).toMatchObject(output);
1212
});

0 commit comments

Comments
 (0)