From 34211f27d1c3ee0d5402bc6d92409588e3ae5913 Mon Sep 17 00:00:00 2001 From: Bartosz Grajdek Date: Mon, 29 Apr 2024 11:23:34 +0200 Subject: [PATCH 1/7] feat: limit accepted syntax for code fence --- lib/ExpensiMark.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ExpensiMark.js b/lib/ExpensiMark.js index ae306472..0f203ef4 100644 --- a/lib/ExpensiMark.js +++ b/lib/ExpensiMark.js @@ -32,7 +32,7 @@ export default class ExpensiMark { name: 'codeFence', // ` is a backtick symbol we are matching on three of them before then after a new line character - regex: /(```(?:\r\n|\n)?)((?:\s*?(?!(?:\r\n|\n)?```(?!`))[\S])+\s*?)((?=(?:\r\n|\n)?)```)/g, + regex: /(```(?:\r\n|\n))((?:\s*?(?!(?:\r\n|\n)?```(?!`))[\S])+\s*?)((?:\r\n|\n)```)/g, // We're using a function here to perform an additional replace on the content // inside the backticks because Android is not able to use
 tags and does
@@ -45,7 +45,7 @@ export default class ExpensiMark {
                     return `
${group}
`; }, rawInputReplacement: (match, __, textWithinFences) => { - const withinFences = match.replace(/(?:```)([\s\S]*?)(?:```)/g, '$1'); + const withinFences = match.replace(/(?:```)([\s\S]*?)(?:```)/g, '$1').replace(/|<\/emoji>/g, ''); const group = textWithinFences.replace(/(?:(?![\n\r])\s)/g, ' '); return `
${group}
`; }, From a4928cb0ee7a7f61ccf5f085eb252cad10f347c2 Mon Sep 17 00:00:00 2001 From: Bartosz Grajdek Date: Mon, 6 May 2024 20:25:51 +0200 Subject: [PATCH 2/7] fix: failing tests --- __tests__/ExpensiMark-HTML-test.js | 156 ++++++++++++----------------- lib/ExpensiMark.js | 2 +- 2 files changed, 64 insertions(+), 94 deletions(-) diff --git a/__tests__/ExpensiMark-HTML-test.js b/__tests__/ExpensiMark-HTML-test.js index eb72e625..82b624b5 100644 --- a/__tests__/ExpensiMark-HTML-test.js +++ b/__tests__/ExpensiMark-HTML-test.js @@ -23,8 +23,8 @@ test('Test multi-line bold markdown replacement', () => { test('Test bold within code blocks is skipped', () => { - const testString = 'bold\n```*not a bold*```\nThis is *bold*'; - const replacedString = 'bold
*not a bold*
This is bold'; + const testString = 'bold\n```\n*not a bold*\n```\nThis is *bold*'; + const replacedString = 'bold
*not a bold*
This is bold'; expect(parser.replace(testString)).toBe(replacedString); }); @@ -202,14 +202,14 @@ test('Test markdown replacement for emails wrapped in bold/strikethrough/italic // Check emails within other markdown test('Test emails within other markdown', () => { const testString = '> test@example.com\n' - + '```test@example.com```\n' + + '```\ntest@example.com\n```\n' + '`test@example.com`\n' + '_test@example.com_ ' + '_test@example.com__ ' + '__test@example.com__ ' + '__test@example.com_'; const result = '
test@example.com
' - + '
test@example.com
' + + '
test@example.com
' + 'test@example.com
' + 'test@example.com ' + 'test@example.com_ ' @@ -412,30 +412,12 @@ test('Test period replacements', () => { test('Test code fencing', () => { let codeFenceExampleMarkdown = '```\nconst javaScript = \'javaScript\'\n```'; expect(parser.replace(codeFenceExampleMarkdown)).toBe('
const javaScript = 'javaScript'
'); - - codeFenceExampleMarkdown = '```const javaScript = \'javaScript\'\n```'; - expect(parser.replace(codeFenceExampleMarkdown)).toBe('
const javaScript = 'javaScript'
'); - - codeFenceExampleMarkdown = '```\nconst javaScript = \'javaScript\'```'; - expect(parser.replace(codeFenceExampleMarkdown)).toBe('
const javaScript = 'javaScript'
'); - - codeFenceExampleMarkdown = '```const javaScript = \'javaScript\'```'; - expect(parser.replace(codeFenceExampleMarkdown)).toBe('
const javaScript = 'javaScript'
'); }); test('Test code fencing with spaces and new lines', () => { let codeFenceExample = '```\nconst javaScript = \'javaScript\'\n const php = \'php\'\n```'; expect(parser.replace(codeFenceExample)).toBe('
const javaScript = 'javaScript'
const php = 'php'
'); - codeFenceExample = '```const javaScript = \'javaScript\'\n const php = \'php\'\n```'; - expect(parser.replace(codeFenceExample)).toBe('
const javaScript = 'javaScript'
const php = 'php'
'); - - codeFenceExample = '```\nconst javaScript = \'javaScript\'\n const php = \'php\'```'; - expect(parser.replace(codeFenceExample)).toBe('
const javaScript = 'javaScript'
const php = 'php'
'); - - codeFenceExample = '```const javaScript = \'javaScript\'\n const php = \'php\'```'; - expect(parser.replace(codeFenceExample)).toBe('
const javaScript = 'javaScript'
const php = 'php'
'); - codeFenceExample = '```\n\n# test\n\n```'; expect(parser.replace(codeFenceExample)).toBe('

# test

'); @@ -490,28 +472,19 @@ test('Test inline code blocks with two backticks', () => { test('Test code fencing with ExpensiMark syntax inside', () => { let codeFenceExample = '```\nThis is how you can write ~strikethrough~, *bold*, _italics_, and [links](https://www.expensify.com)\n```'; expect(parser.replace(codeFenceExample)).toBe('
This is how you can write ~strikethrough~, *bold*, _italics_, and [links](https://www.expensify.com)
'); - - codeFenceExample = '```This is how you can write ~strikethrough~, *bold*, _italics_, and [links](https://www.expensify.com)\n```'; - expect(parser.replace(codeFenceExample)).toBe('
This is how you can write ~strikethrough~, *bold*, _italics_, and [links](https://www.expensify.com)
'); - - codeFenceExample = '```\nThis is how you can write ~strikethrough~, *bold*, _italics_, and [links](https://www.expensify.com)```'; - expect(parser.replace(codeFenceExample)).toBe('
This is how you can write ~strikethrough~, *bold*, _italics_, and [links](https://www.expensify.com)
'); - - codeFenceExample = '```This is how you can write ~strikethrough~, *bold*, _italics_, and [links](https://www.expensify.com)```'; - expect(parser.replace(codeFenceExample)).toBe('
This is how you can write ~strikethrough~, *bold*, _italics_, and [links](https://www.expensify.com)
'); }); test('Test code fencing with ExpensiMark syntax outside', () => { - let codeFenceExample = '# Test1 ```code``` Test2'; - expect(parser.replace(codeFenceExample)).toBe('

Test1

code
Test2'); + let codeFenceExample = '# Test1 ```\ncode\n``` Test2'; + expect(parser.replace(codeFenceExample)).toBe('

Test1

code
Test2'); - codeFenceExample = '*Test1 ```code``` Test2*'; - expect(parser.replace(codeFenceExample)).toBe('*Test1
code
Test2*'); + codeFenceExample = '*Test1 ```\ncode\n``` Test2*'; + expect(parser.replace(codeFenceExample)).toBe('*Test1
code
Test2*'); expect(parser.replace(codeFenceExample, {shouldKeepRawInput: true})).toBe('*Test1
code
Test2*'); - codeFenceExample = '_Test1 ```code``` Test2_'; - expect(parser.replace(codeFenceExample)).toBe('_Test1
code
Test2_'); - expect(parser.replace(codeFenceExample, {shouldKeepRawInput: true})).toBe('_Test1
code
Test2_'); + codeFenceExample = '_Test1 ```\ncode\n``` Test2_'; + expect(parser.replace(codeFenceExample)).toBe('_Test1
code
Test2_'); + expect(parser.replace(codeFenceExample, {shouldKeepRawInput: true})).toBe('_Test1
code
Test2_'); codeFenceExample = '~Test1 ```code``` Test2~'; expect(parser.replace(codeFenceExample)).toBe('~Test1
code
Test2~'); @@ -523,23 +496,20 @@ test('Test code fencing with ExpensiMark syntax outside', () => { }); test('Test code fencing with additional backticks inside', () => { - let nestedBackticks = '````test````'; - expect(parser.replace(nestedBackticks)).toBe('
`test`
'); - - nestedBackticks = '````\ntest\n````'; - expect(parser.replace(nestedBackticks)).toBe('
`
test
`
'); + let nestedBackticks = '```\n`test`\n```'; + expect(parser.replace(nestedBackticks)).toBe('
`test`
'); - nestedBackticks = '````````'; - expect(parser.replace(nestedBackticks)).toBe('
``
'); + nestedBackticks = '```\n`\ntest\n`\n```'; + expect(parser.replace(nestedBackticks)).toBe('
`
test
`
'); - nestedBackticks = '````\n````'; - expect(parser.replace(nestedBackticks)).toBe('
`
`
'); + nestedBackticks = '```\n``\n```'; + expect(parser.replace(nestedBackticks)).toBe('
``
'); - nestedBackticks = '```````````'; - expect(parser.replace(nestedBackticks)).toBe('
`````
'); + nestedBackticks = '```\n`\n`\n```'; + expect(parser.replace(nestedBackticks)).toBe('
`
`
'); - nestedBackticks = '````This is how you can write ~strikethrough~, *bold*, _italics_, and [links](https://www.expensify.com)````'; - expect(parser.replace(nestedBackticks)).toBe('
`This is how you can write ~strikethrough~, *bold*, _italics_, and [links](https://www.expensify.com)`
'); + nestedBackticks = '```\n`This is how you can write ~strikethrough~, *bold*, _italics_, and [links](https://www.expensify.com)`\n```'; + expect(parser.replace(nestedBackticks)).toBe('
`This is how you can write ~strikethrough~, *bold*, _italics_, and [links](https://www.expensify.com)`
'); }); test('Test combination replacements', () => { @@ -1016,41 +986,41 @@ test('Test autolink replacement to avoid parsing nested links', () => { }); test('Test quotes markdown replacement with text matching inside and outside codefence without spaces', () => { - const testString = 'The next line should be quoted\n>Hello,I’mtext\n```\nThe next line should not be quoted\n>Hello,I’mtext\nsince its inside a codefence```'; + const testString = 'The next line should be quoted\n>Hello,I’mtext\n```\nThe next line should not be quoted\n>Hello,I’mtext\nsince its inside a codefence\n```'; - const resultString = 'The next line should be quoted
Hello,I’mtext
The next line should not be quoted
>Hello,I’mtext
since its inside a codefence
'; + const resultString = 'The next line should be quoted
Hello,I’mtext
The next line should not be quoted
>Hello,I’mtext
since its inside a codefence
'; expect(parser.replace(testString)).toBe(resultString); }); test('Test quotes markdown replacement with text matching inside and outside codefence at the same line', () => { - const testString = 'The next line should be quoted\n>Hello,I’mtext\nThe next line should not be quoted\n```>Hello,I’mtext```\nsince its inside a codefence'; + const testString = 'The next line should be quoted\n>Hello,I’mtext\nThe next line should not be quoted\n```\n>Hello,I’mtext\n```\nsince its inside a codefence'; - const resultString = 'The next line should be quoted
Hello,I’mtext
The next line should not be quoted
>Hello,I’mtext
since its inside a codefence'; + const resultString = 'The next line should be quoted
Hello,I’mtext
The next line should not be quoted
>Hello,I’mtext
since its inside a codefence'; expect(parser.replace(testString)).toBe(resultString); }); test('Test quotes markdown replacement with text matching inside and outside codefence at the end of the text', () => { - const testString = 'The next line should be quoted\n>Hello,I’mtext\nThe next line should not be quoted\n```>Hello,I’mtext```'; + const testString = 'The next line should be quoted\n>Hello,I’mtext\nThe next line should not be quoted\n```\n>Hello,I’mtext\n```'; - const resultString = 'The next line should be quoted
Hello,I’mtext
The next line should not be quoted
>Hello,I’mtext
'; + const resultString = 'The next line should be quoted
Hello,I’mtext
The next line should not be quoted
>Hello,I’mtext
'; expect(parser.replace(testString)).toBe(resultString); }); test('Test quotes markdown replacement with text matching inside and outside codefence with quotes at the end of the text', () => { - const testString = 'The next line should be quoted\n```>Hello,I’mtext```\nThe next line should not be quoted\n>Hello,I’mtext'; + const testString = 'The next line should be quoted\n```\n>Hello,I’mtext\n```\nThe next line should not be quoted\n>Hello,I’mtext'; - const resultString = 'The next line should be quoted
>Hello,I’mtext
The next line should not be quoted
Hello,I’mtext
'; + const resultString = 'The next line should be quoted
>Hello,I’mtext
The next line should not be quoted
Hello,I’mtext
'; expect(parser.replace(testString)).toBe(resultString); }); test('Test quotes markdown replacement and removing
from
 and 

', () => { - const testString = 'The next line should be quoted\n```>Hello,I’mtext```\nThe next line should not be quoted'; + const testString = 'The next line should be quoted\n```\n>Hello,I’mtext\n```\nThe next line should not be quoted'; - const resultString = 'The next line should be quoted
>Hello,I’mtext
The next line should not be quoted'; + const resultString = 'The next line should be quoted
>Hello,I’mtext
The next line should not be quoted'; expect(parser.replace(testString)).toBe(resultString); }); @@ -1255,8 +1225,8 @@ test('Test for user mention with invalid username', () => { }); test('Test for user mention with codefence style', () => { - const testString = '```@username@expensify.com```'; - const resultString = '
@username@expensify.com
'; + const testString = '```\n@username@expensify.com\n```'; + const resultString = '
@username@expensify.com
'; expect(parser.replace(testString)).toBe(resultString); }); @@ -1267,8 +1237,8 @@ test('Test for user mention with inlineCodeBlock style', () => { }); test('Test for user mention with text with codefence style', () => { - const testString = '```hi @username@expensify.com```'; - const resultString = '
hi @username@expensify.com
'; + const testString = '```\nhi @username@expensify.com\n```'; + const resultString = '
hi @username@expensify.com
'; expect(parser.replace(testString)).toBe(resultString); }); @@ -1297,8 +1267,8 @@ test('Test for user mention with user email includes underscores', () => { }); test('Test for @here mention with codefence style', () => { - const testString = '```@here```'; - const resultString = '
@here
'; + const testString = '```\n@here\n```'; + const resultString = '
@here
'; expect(parser.replace(testString)).toBe(resultString); }); @@ -1560,11 +1530,11 @@ test('Test here mention with @here@here', () => { }); test('Test link with code fence inside the alias text part', () => { - const testString = '[```code```](google.com) ' - + '[test ```code``` test](google.com)'; + const testString = '[```\ncode\n```](google.com) ' + + '[test ```\ncode\n``` test](google.com)'; - const resultString = '[
code
](google.com) ' - + '[test
code
test](google.com)'; + const resultString = '[
code
](google.com) ' + + '[test
code
test](google.com)'; expect(parser.replace(testString)).toBe(resultString); }); @@ -1606,20 +1576,20 @@ test('Linebreak between end of text and start of code block should be remained', resultString: '|
code
', }, { - testString: 'text1```code```text2', - resultString: 'text1
code
text2', + testString: 'text1```\ncode\n```text2', + resultString: 'text1
code
text2', }, { - testString: 'text1 ``` code ``` text2', - resultString: 'text1
 code 
text2', + testString: 'text1 ```\n code \n``` text2', + resultString: 'text1
 code 
text2', }, { - testString: 'text1\n```code```\ntext2', - resultString: 'text1
code
text2', + testString: 'text1\n```\ncode\n```\ntext2', + resultString: 'text1
code
text2', }, { - testString: 'text1\n``` code ```\ntext2', - resultString: 'text1
 code 
text2', + testString: 'text1\n```\n code \n```\ntext2', + resultString: 'text1
 code 
text2', }, { testString: 'text1\n```\n\ncode\n```\ntext2', @@ -1645,24 +1615,24 @@ test('Linebreak between end of text and start of code block should be remained', test('Test autoEmail with markdown of
, , ,  and  tag', () => {
     const testString = '`code`test@gmail.com '
-        + '```code block```test@gmail.com '
+        + '```\ncode block\n```test@gmail.com '
         + '[Google](https://google.com)test@gmail.com '
         + '_test@gmail.com_ '
         + '_test\n\ntest@gmail.com_ '
         + '`test@expensify.com` '
-        + '```test@expensify.com``` '
+        + '```\ntest@expensify.com\n``` '
         + '@test@expensify.com '
         + '_@username@expensify.com_ '
         + '[https://staging.new.expensify.com/details/test@expensify.com](https://staging.new.expensify.com/details/test@expensify.com) '
         + '[test italic style wrap email _test@gmail.com_ inside a link](https://google.com) ';
 
     const resultString = 'codetest@gmail.com '
-        + '
code block
test@gmail.com ' + + '
code block
test@gmail.com ' + 'Googletest@gmail.com ' + 'test@gmail.com ' + 'test

test@gmail.com
' + 'test@expensify.com ' - + '
test@expensify.com
' + + '
test@expensify.com
' + '@test@expensify.com ' + '@username@expensify.com ' + 'https://staging.new.expensify.com/details/test@expensify.com ' @@ -1817,20 +1787,20 @@ describe('when should keep raw input flag is enabled', () => { }); test('Test code fence within inline code', () => { - let testString = 'Hello world `(```test```)` Hello world'; - expect(parser.replace(testString)).toBe('Hello world `(
test
)` Hello world'); + let testString = 'Hello world `(```\ntest\n```)` Hello world'; + expect(parser.replace(testString)).toBe('Hello world `(
test
)` Hello world'); - testString = 'Hello world `(```test\ntest```)` Hello world'; - expect(parser.replace(testString)).toBe('Hello world `(
test
test
)` Hello world'); + testString = 'Hello world `(```\ntest\ntest\n```)` Hello world'; + expect(parser.replace(testString)).toBe('Hello world `(
test
test
)` Hello world'); - testString = 'Hello world ```(`test`)``` Hello world'; - expect(parser.replace(testString)).toBe('Hello world
(`test`)
Hello world'); + testString = 'Hello world ```\n(`test`)\n``` Hello world'; + expect(parser.replace(testString)).toBe('Hello world
(`test`)
Hello world'); - testString = 'Hello world `test`space```block``` Hello world'; - expect(parser.replace(testString)).toBe('Hello world testspace
block
Hello world'); + testString = 'Hello world `test`space```\nblock\n``` Hello world'; + expect(parser.replace(testString)).toBe('Hello world testspace
block
Hello world'); - testString = 'Hello world ```block```space`test` Hello world'; - expect(parser.replace(testString)).toBe('Hello world
block
spacetest Hello world'); + testString = 'Hello world ```\nblock\n```space`test` Hello world'; + expect(parser.replace(testString)).toBe('Hello world
block
spacetest Hello world'); }); test('Test italic/bold/strikethrough markdown to keep consistency', () => { diff --git a/lib/ExpensiMark.js b/lib/ExpensiMark.js index 0f203ef4..6db0d6e1 100644 --- a/lib/ExpensiMark.js +++ b/lib/ExpensiMark.js @@ -32,7 +32,7 @@ export default class ExpensiMark { name: 'codeFence', // ` is a backtick symbol we are matching on three of them before then after a new line character - regex: /(```(?:\r\n|\n))((?:\s*?(?!(?:\r\n|\n)?```(?!`))[\S])+\s*?)((?:\r\n|\n)```)/g, + regex: /(```(?:\r\n|\n))((?:\s*?(?!(?:\r\n|\n)?```(?!`))[\S])+\s*?(?:\r\n|\n))(```)/g, // We're using a function here to perform an additional replace on the content // inside the backticks because Android is not able to use
 tags and does

From e250526be471bfa243f31fca91a2611ab3486cb1 Mon Sep 17 00:00:00 2001
From: Robert Kozik 
Date: Mon, 6 May 2024 21:37:26 +0200
Subject: [PATCH 3/7] change inline code regex to match triple tick in one line

---
 __tests__/ExpensiMark-HTML-test.js | 15 +++++++++++++++
 lib/ExpensiMark.js                 | 12 ++----------
 2 files changed, 17 insertions(+), 10 deletions(-)

diff --git a/__tests__/ExpensiMark-HTML-test.js b/__tests__/ExpensiMark-HTML-test.js
index 82b624b5..7f7fc915 100644
--- a/__tests__/ExpensiMark-HTML-test.js
+++ b/__tests__/ExpensiMark-HTML-test.js
@@ -439,6 +439,21 @@ test('Test inline code blocks', () => {
     expect(parser.replace(inlineCodeStartString)).toBe('My favorite language is JavaScript. How about you?');
 });
 
+test('Test inline code blocks with double backticks', () => {
+    const inlineCodeStartString = 'My favorite language is ``JavaScript``. How about you?';
+    expect(parser.replace(inlineCodeStartString)).toBe('My favorite language is `JavaScript`. How about you?');
+});
+
+test('Test inline code blocks with triple backticks', () => {
+    const inlineCodeStartString = 'My favorite language is ```JavaScript```. How about you?';
+    expect(parser.replace(inlineCodeStartString)).toBe('My favorite language is ``JavaScript``. How about you?');
+});
+
+test('Test multiple inline codes in one line', () => {
+    const inlineCodeStartString = 'My favorite language is `JavaScript`. How about you? I also like `Python`.';
+    expect(parser.replace(inlineCodeStartString)).toBe('My favorite language is JavaScript. How about you? I also like Python.');
+});
+
 test('Test inline code with one backtick as content', () => {
     const inlineCodeStartString = '```';
     expect(parser.replace(inlineCodeStartString)).toBe('```');
diff --git a/lib/ExpensiMark.js b/lib/ExpensiMark.js
index 6db0d6e1..34a29994 100644
--- a/lib/ExpensiMark.js
+++ b/lib/ExpensiMark.js
@@ -61,16 +61,8 @@ export default class ExpensiMark {
                 // Use the url escaped version of a backtick (`) symbol. Mobile platforms do not support lookbehinds,
                 // so capture the first and third group and place them in the replacement.
                 // but we should not replace backtick symbols if they include 
 tags between them.
-                regex: /(\B|_|)`(?:(?!(?:(?!`).)*?
))(.*?\S.*?)`(\B|_|)(?!`|[^<]*<\/pre>)/g,
-                replacement: (match, g1, g2, g3) => {
-                    const regex = /^[`]+$/i;
-
-                    // if content of the inline code block is only backtick symbols, we should not replace them with  tag
-                    if (regex.test(g2)) {
-                        return match;
-                    }
-                    return `${g1}${g2}${g3}`;
-                },
+                regex: /(\B|_|)`(.*?(?![`])\S.*?)`(\B|_|)(?!`|[^<]*<\/pre>)/gm,
+                replacement: '$1$2$3',
             },
 
             /**

From 5857bd2251755cabe19926bd93b76da37f3f155b Mon Sep 17 00:00:00 2001
From: Bartosz Grajdek 
Date: Tue, 7 May 2024 12:56:49 +0200
Subject: [PATCH 4/7] fix: tests related to shouldKeepRawInput & code fence

---
 __tests__/ExpensiMark-HTML-test.js | 20 ++++++++++----------
 lib/ExpensiMark.js                 |  4 ++--
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/__tests__/ExpensiMark-HTML-test.js b/__tests__/ExpensiMark-HTML-test.js
index 7f7fc915..ebbdc5b1 100644
--- a/__tests__/ExpensiMark-HTML-test.js
+++ b/__tests__/ExpensiMark-HTML-test.js
@@ -410,7 +410,7 @@ test('Test period replacements', () => {
 });
 
 test('Test code fencing', () => {
-    let codeFenceExampleMarkdown = '```\nconst javaScript = \'javaScript\'\n```';
+    const codeFenceExampleMarkdown = '```\nconst javaScript = \'javaScript\'\n```';
     expect(parser.replace(codeFenceExampleMarkdown)).toBe('
const javaScript = 'javaScript'
'); }); @@ -485,7 +485,7 @@ test('Test inline code blocks with two backticks', () => { }); test('Test code fencing with ExpensiMark syntax inside', () => { - let codeFenceExample = '```\nThis is how you can write ~strikethrough~, *bold*, _italics_, and [links](https://www.expensify.com)\n```'; + const codeFenceExample = '```\nThis is how you can write ~strikethrough~, *bold*, _italics_, and [links](https://www.expensify.com)\n```'; expect(parser.replace(codeFenceExample)).toBe('
This is how you can write ~strikethrough~, *bold*, _italics_, and [links](https://www.expensify.com)
'); }); @@ -495,19 +495,19 @@ test('Test code fencing with ExpensiMark syntax outside', () => { codeFenceExample = '*Test1 ```\ncode\n``` Test2*'; expect(parser.replace(codeFenceExample)).toBe('*Test1
code
Test2*'); - expect(parser.replace(codeFenceExample, {shouldKeepRawInput: true})).toBe('*Test1
code
Test2*'); + expect(parser.replace(codeFenceExample, {shouldKeepRawInput: true})).toBe('*Test1
code\n
Test2*'); codeFenceExample = '_Test1 ```\ncode\n``` Test2_'; expect(parser.replace(codeFenceExample)).toBe('_Test1
code
Test2_'); - expect(parser.replace(codeFenceExample, {shouldKeepRawInput: true})).toBe('_Test1
code
Test2_'); + expect(parser.replace(codeFenceExample, {shouldKeepRawInput: true})).toBe('_Test1
code\n
Test2_'); - codeFenceExample = '~Test1 ```code``` Test2~'; - expect(parser.replace(codeFenceExample)).toBe('~Test1
code
Test2~'); - expect(parser.replace(codeFenceExample, {shouldKeepRawInput: true})).toBe('~Test1
code
Test2~'); + codeFenceExample = '~Test1 ```\ncode\n``` Test2~'; + expect(parser.replace(codeFenceExample)).toBe('~Test1
code
Test2~'); + expect(parser.replace(codeFenceExample, {shouldKeepRawInput: true})).toBe('~Test1
code\n
Test2~'); - codeFenceExample = '[```code```](google.com)'; - expect(parser.replace(codeFenceExample)).toBe('[
code
](google.com)'); - expect(parser.replace(codeFenceExample, {shouldKeepRawInput: true})).toBe('[
code
](google.com)'); + codeFenceExample = '[```\ncode\n```](google.com)'; + expect(parser.replace(codeFenceExample)).toBe('[
code
](google.com)'); + expect(parser.replace(codeFenceExample, {shouldKeepRawInput: true})).toBe('[
code\n
](google.com)'); }); test('Test code fencing with additional backticks inside', () => { diff --git a/lib/ExpensiMark.js b/lib/ExpensiMark.js index 34a29994..025e0740 100644 --- a/lib/ExpensiMark.js +++ b/lib/ExpensiMark.js @@ -32,7 +32,7 @@ export default class ExpensiMark { name: 'codeFence', // ` is a backtick symbol we are matching on three of them before then after a new line character - regex: /(```(?:\r\n|\n))((?:\s*?(?!(?:\r\n|\n)?```(?!`))[\S])+\s*?(?:\r\n|\n))(```)/g, + regex: /(```(?:\r\n|\n))((?:\s*?(?!(?:\r\n|\n)?```(?!`))[\S])+\s*?(?:\r\n|\n))(```)/g, // We're using a function here to perform an additional replace on the content // inside the backticks because Android is not able to use
 tags and does
@@ -45,7 +45,7 @@ export default class ExpensiMark {
                     return `
${group}
`; }, rawInputReplacement: (match, __, textWithinFences) => { - const withinFences = match.replace(/(?:```)([\s\S]*?)(?:```)/g, '$1').replace(/|<\/emoji>/g, ''); + const withinFences = match.replace(/(?:```)([\s\S]*?)(?:```)/g, '$1'); const group = textWithinFences.replace(/(?:(?![\n\r])\s)/g, ' '); return `
${group}
`; }, From 1f9d3f3f57985e11b6b090ab8ef274ab4a3d3b43 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Mon, 27 May 2024 16:41:49 +0200 Subject: [PATCH 5/7] fix user mention not deleting phone domain --- lib/ExpensiMark.js | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/ExpensiMark.js b/lib/ExpensiMark.js index 4a6a1fe9..415f70fb 100644 --- a/lib/ExpensiMark.js +++ b/lib/ExpensiMark.js @@ -475,15 +475,18 @@ export default class ExpensiMark { }, { name: 'userMention', - regex: //gi, - replacement: (match, g1, offset, string, extras) => { - const accountToNameMap = extras.accountIdToName; - if (!accountToNameMap || !accountToNameMap[g1]) { - Log.alert('[ExpensiMark] Missing account name', {accountID: g1}); - return '@Hidden'; + regex: /(?:)|(?:(.*?)<\/mention-user>)/gi, + replacement: (match, g1, g2, offset, string, extras) => { + if (g1) { + const accountToNameMap = extras.accountIdToName; + if (!accountToNameMap || !accountToNameMap[g1]) { + Log.alert('[ExpensiMark] Missing account name', {accountID: g1}); + return '@Hidden'; + } + + return `@${extras.accountIdToName[g1]}`; } - - return `@${extras.accountIdToName[g1]}`; + return Str.removeSMSDomain(g2) }, }, ]; From 7cb4212723984ed1c511397c058ada83617a2583 Mon Sep 17 00:00:00 2001 From: Jakub Szymczak Date: Mon, 27 May 2024 17:04:41 +0200 Subject: [PATCH 6/7] add test case to phone number mention --- __tests__/ExpensiMark-Markdown-test.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/__tests__/ExpensiMark-Markdown-test.js b/__tests__/ExpensiMark-Markdown-test.js index 0f8e8831..8f446820 100644 --- a/__tests__/ExpensiMark-Markdown-test.js +++ b/__tests__/ExpensiMark-Markdown-test.js @@ -756,6 +756,10 @@ test('Mention user html to markdown', () => { testString = '@user@DOMAIN.com'; expect(parser.htmlToMarkdown(testString)).toBe('@user@DOMAIN.com'); + // When there is a phone number mention the sms domain `@expensify.sms`should be removed from returned string + testString = '@+311231231@expensify.sms'; + expect(parser.htmlToMarkdown(testString)).toBe('@+311231231'); + // When there is `accountID` and no `extras`, `@Hidden` should be returned testString = ''; expect(parser.htmlToMarkdown(testString)).toBe('@Hidden'); From 321e4a7f5e618000be812d5b7dc76d00f81084f9 Mon Sep 17 00:00:00 2001 From: OSBotify Date: Tue, 28 May 2024 00:07:18 +0000 Subject: [PATCH 7/7] 2.0.1 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8c8c3f38..daf95dee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "expensify-common", - "version": "2.0.0", + "version": "2.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "expensify-common", - "version": "2.0.0", + "version": "2.0.1", "license": "MIT", "dependencies": { "classnames": "2.5.0", diff --git a/package.json b/package.json index a3b13a6c..4df2f8da 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "expensify-common", - "version": "2.0.0", + "version": "2.0.1", "author": "Expensify, Inc.", "description": "Expensify libraries and components shared across different repos", "homepage": "https://expensify.com",