From 41a35758056a462fb5a3263072c7d3958b9d7a92 Mon Sep 17 00:00:00 2001 From: alina sireneva Date: Thu, 30 May 2024 00:07:47 +0300 Subject: [PATCH] fix(markdown-parser): handle interpolation inside links --- packages/markdown-parser/src/index.ts | 117 +++++++++++------- .../src/markdown-parser.test.ts | 22 ++++ 2 files changed, 94 insertions(+), 45 deletions(-) diff --git a/packages/markdown-parser/src/index.ts b/packages/markdown-parser/src/index.ts index 63195e86..8c269dce 100644 --- a/packages/markdown-parser/src/index.ts +++ b/packages/markdown-parser/src/index.ts @@ -146,6 +146,9 @@ function parse( let insidePre = false let insideLink = false + let insideLinkUrl = false + let pendingLinkUrl = '' + function feed(text: string) { const len = text.length let pos = 0 @@ -154,7 +157,11 @@ function parse( const c = text[pos] if (c === '\\') { - result += text[pos + 1] + if (insideLinkUrl) { + pendingLinkUrl += text[pos + 1] + } else { + result += text[pos + 1] + } pos += 2 continue } @@ -220,51 +227,58 @@ function parse( } pos += 2 - let url = '' - - while (pos < text.length && text[pos] !== ')') { - url += text[pos++] - } - - pos += 1 // ) - - if (pos > text.length) { - throw new Error('Malformed LINK entity, expected )') - } - - if (url.length) { - ent.length = result.length - ent.offset - - let m = url.match(MENTION_REGEX) - - if (m) { - const userId = parseInt(m[1]) - const accessHash = m[2] - - if (accessHash) { - (ent as tl.Mutable)._ = - 'inputMessageEntityMentionName' - ;(ent as tl.Mutable).userId = { - _: 'inputUser', - userId, - accessHash: Long.fromString(accessHash, false, 16), - } - } else { - (ent as tl.Mutable)._ = 'messageEntityMentionName' - ;(ent as tl.Mutable).userId = userId - } - } else if ((m = EMOJI_REGEX.exec(url))) { - (ent as tl.Mutable)._ = 'messageEntityCustomEmoji' - ;(ent as tl.Mutable).documentId = Long.fromString(m[1]) - } else { - if (url.match(/^\/\//)) url = 'http:' + url - ;(ent as tl.Mutable)._ = 'messageEntityTextUrl' - ;(ent as tl.Mutable).url = url - } - entities.push(ent) - } - insideLink = false + insideLinkUrl = true + stacks.link.push(ent) + continue + } + + if (insideLinkUrl) { + pos += 1 + + if (c !== ')') { + // not ended yet + pendingLinkUrl += c + continue + } + + const ent = stacks.link.pop()! + + let url = pendingLinkUrl + pendingLinkUrl = '' + insideLinkUrl = false + + if (!url.length) continue + + ent.length = result.length - ent.offset + + let m = url.match(MENTION_REGEX) + + if (m) { + const userId = parseInt(m[1]) + const accessHash = m[2] + + if (accessHash) { + (ent as tl.Mutable)._ = 'inputMessageEntityMentionName' + ;(ent as tl.Mutable).userId = { + _: 'inputUser', + userId, + accessHash: Long.fromString(accessHash, false, 16), + } + } else { + (ent as tl.Mutable)._ = 'messageEntityMentionName' + ;(ent as tl.Mutable).userId = userId + } + } else if ((m = EMOJI_REGEX.exec(url))) { + (ent as tl.Mutable)._ = 'messageEntityCustomEmoji' + ;(ent as tl.Mutable).documentId = Long.fromString(m[1]) + } else { + if (url.match(/^\/\//)) url = 'http:' + url + ;(ent as tl.Mutable)._ = 'messageEntityTextUrl' + ;(ent as tl.Mutable).url = url + } + entities.push(ent) + continue } @@ -395,6 +409,19 @@ function parse( if (typeof it === 'boolean' || !it) return + if (insideLinkUrl) { + if (typeof it === 'string' || typeof it === 'number') { + pendingLinkUrl += it + } else if (Long.isLong(it)) { + pendingLinkUrl += it.toString(10) + } else { + // ignore the entity, only use text + pendingLinkUrl += it.text + } + + return + } + if (typeof it === 'string' || typeof it === 'number') { result += it } else if (Long.isLong(it)) { diff --git a/packages/markdown-parser/src/markdown-parser.test.ts b/packages/markdown-parser/src/markdown-parser.test.ts index 45a13c39..97a2db87 100644 --- a/packages/markdown-parser/src/markdown-parser.test.ts +++ b/packages/markdown-parser/src/markdown-parser.test.ts @@ -514,6 +514,11 @@ describe('MarkdownMessageEntityParser', () => { test(md_`${'**plain**'}`, [], '**plain**') }) + it('should handle numbers/Longs', () => { + test(md_`${1234567890}`, [], '1234567890') + test(md_`${Long.fromString('1234567890')}`, [], '1234567890') + }) + it('should skip falsy values', () => { test(md_`some text ${null} more text ${false}`, [], 'some text more text ') }) @@ -579,6 +584,23 @@ describe('MarkdownMessageEntityParser', () => { ) }) + it('should interpolate inside links', () => { + test( + md_`[${'link'}](${'https'}://example.com/\\)${'foo'}/bar/${'baz'}?foo=bar&baz=${'egg'})`, + [ + createEntity('messageEntityTextUrl', 0, 4, { + url: 'https://example.com/)foo/bar/baz?foo=bar&baz=egg', + }), + ], + 'link', + ) + test( + md_`[emoji](tg://emoji?id=${Long.fromNumber(123123)})`, + [createEntity('messageEntityCustomEmoji', 0, 5, { documentId: Long.fromNumber(123123) })], + 'emoji', + ) + }) + it('should process MessageEntity', () => { test( md_`**bold ${new MessageEntity(createEntity('messageEntityItalic', 0, 11), 'bold italic')} more bold**`,