diff --git a/__tests__/ExpensiMark-HTML-test.js b/__tests__/ExpensiMark-HTML-test.js
index 7f0ba5a3..a98e35aa 100644
--- a/__tests__/ExpensiMark-HTML-test.js
+++ b/__tests__/ExpensiMark-HTML-test.js
@@ -84,31 +84,13 @@ test('Test multi-line strikethrough markdown replacement', () => {
expect(parser.replace(testString)).toBe(replacedString);
});
-// Emails containing *_~ are successfully wrapped in a mailto anchor tag
+// Emails containing _ are successfully wrapped in a mailto anchor tag
test('Test markdown replacement for emails and email links containing bold/strikethrough/italic', () => {
- let testInput = 'a~b@gmail.com';
- expect(parser.replace(testInput)).toBe('a~b@gmail.com');
-
- testInput = 'a*b@gmail.com';
- expect(parser.replace(testInput)).toBe('a*b@gmail.com');
-
- testInput = 'a_b@gmail.com';
+ let testInput = 'a_b@gmail.com';
expect(parser.replace(testInput)).toBe('a_b@gmail.com');
- testInput = 'a~*_b@gmail.com';
- expect(parser.replace(testInput)).toBe('a~*_b@gmail.com');
-
- testInput = '[text](a~b@gmail.com)';
- expect(parser.replace(testInput)).toBe('text');
-
- testInput = '[text](a*b@gmail.com)';
- expect(parser.replace(testInput)).toBe('text');
-
testInput = '[text](a_b@gmail.com)';
expect(parser.replace(testInput)).toBe('text');
-
- testInput = '[text](a~*_b@gmail.com)';
- expect(parser.replace(testInput)).toBe('text');
});
// Single-line emails wrapped in *_~ are successfully wrapped in a mailto anchor tag
@@ -160,6 +142,12 @@ test('Test markdown replacement for emails wrapped in bold/strikethrough/italic
+ 'def@gmail.com';
expect(parser.replace(testInput)).toBe(result);
+ testInput = '_email@test.com\n_email2@test.com\n\nemail3@test.com_';
+ result = 'email@test.com
'
+ + '_email2@test.com
'
+ + 'email3@test.com';
+ expect(parser.replace(testInput)).toBe(result);
+
testInput = '[text](~abc@gmail.com)\n[text](def@gmail.com~)';
result = '[text](abc@gmail.com))';
@@ -171,8 +159,8 @@ test('Test markdown replacement for emails wrapped in bold/strikethrough/italic
expect(parser.replace(testInput)).toBe(result);
testInput = '[text](_abc@gmail.com)\n[text](def@gmail.com_)';
- result = '[text](abc@gmail.com)
'
+ '[text](def@gmail.com
'
- + '[text](def@gmail.com)';
+ result = 'text
'
+ + '[text](def@gmail.com_)';
expect(parser.replace(testInput)).toBe(result);
testInput = '[text](~*_abc@gmail.com)\n[text](def@gmail.com_*~)';
@@ -181,6 +169,108 @@ test('Test markdown replacement for emails wrapped in bold/strikethrough/italic
expect(parser.replace(testInput)).toBe(result);
});
+// Check emails within other markdown
+test('Test emails within other markdown', () => {
+ const testString = '> test@example.com\n'
+ + '```test@example.com```\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
') || this.containsNonPairTag(textWithinUnderscores)) { + return match; + } + if (String(textWithinUnderscores).match(`^${CONST.REG_EXP.MARKDOWN_EMAIL}`)) { + return `${extraLeadingUnderscores}${textWithinUnderscores}`; + } + return `${extraLeadingUnderscores}${textWithinUnderscores}`; + }, + }, + + /** + * Automatically links emails that are not in a link. Runs before the autolinker as it will not link an + * email that is in a link + * Prevent emails from starting with [~_*]. Such emails should not be supported. + */ + { + name: 'autoEmail', + regex: new RegExp( + `(?![^<]*>|[^<>]*<\\/(?!em))([^\\w'#%+-]|^|)${CONST.REG_EXP.MARKDOWN_EMAIL}(?!((?:(?!|[^<]*(<\\/pre>|<\\/code>))`, + 'gim', + ), + replacement: '$1$2', + }, + { name: 'heading1', regex: /^# +(?! )((?:(?!|\n|\r\n).)+)/gm, @@ -175,26 +200,6 @@ export default class ExpensiMark { return `${replacedText}`; }, }, - { - /** - * Use \b in this case because it will match on words, letters, - * and _: https://www.rexegg.com/regex-boundaries.html#wordboundary - * The !_blank is to prevent the `target="_blank">` section of the - * link replacement from being captured Additionally, something like - * `\b\_([^<>]*?)\_\b` doesn't work because it won't replace - * `_https://www.test.com_` - * Use [\s\S]* instead of .* to match newline - */ - name: 'italic', - regex: /(\b_+|\b)(?!_blank")_((?![\s_])[\s\S]*?[^\s_])_(?![^\W_])(?![^<]*(<\/pre>|<\/code>|<\/a>|<\/mention-user>|_blank))/g, - - // We want to add extraLeadingUnderscores back before the tag - replacement: (match, extraLeadingUnderscores, textWithinUnderscores) => ( - textWithinUnderscores.includes('') || this.containsNonPairTag(textWithinUnderscores) - ? match - : `${extraLeadingUnderscores}${textWithinUnderscores}` - ), - }, { // Use \B in this case because \b doesn't match * or ~. // \B will match everything that \b doesn't, so it works diff --git a/lib/Url.js b/lib/Url.js index f8eb7e9e..12c53af1 100644 --- a/lib/Url.js +++ b/lib/Url.js @@ -2,7 +2,7 @@ import TLD_REGEX from './tlds'; const ALLOWED_PORTS = '([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])'; const URL_PROTOCOL_REGEX = '((ht|f)tps?:\\/\\/)'; -const URL_WEBSITE_REGEX = `${URL_PROTOCOL_REGEX}?((?:www\\.)?[a-z0-9](?:[-a-z0-9]*[a-z0-9])?\\.)+(?:${TLD_REGEX})(?:\\:${ALLOWED_PORTS}|\\b|(?=_))`; +const URL_WEBSITE_REGEX = `${URL_PROTOCOL_REGEX}?((?:www\\.)?[a-z0-9](?:[-a-z0-9]*[a-z0-9])?\\.)+(?:${TLD_REGEX})(?:\\:${ALLOWED_PORTS}|\\b|(?=_))(?!@(?:[a-z\\d-]+\\.)+[a-z]{2,})`; const addEscapedChar = reg => `(?:${reg}|&(?:amp|quot|#x27);)`; const URL_PATH_REGEX = `(?:${addEscapedChar('[.,=(+$!*]')}?\\/${addEscapedChar('[-\\w$@.+!*:(),=%~]')}*${addEscapedChar('[-\\w~@:%)]')}|\\/)*`; const URL_PARAM_REGEX = `(?:\\?${addEscapedChar('[-\\w$@.+!*()\\/,=%{}:;\\[\\]\\|_|~]')}*)?`;