fix(html-parser): interpolating inside attribs
This commit is contained in:
parent
81c8dab2a6
commit
7a6d984977
2 changed files with 73 additions and 0 deletions
|
@ -501,6 +501,41 @@ describe('HtmlMessageEntityParser', () => {
|
|||
)
|
||||
})
|
||||
|
||||
it('should handle interpolation into attrs', () => {
|
||||
test(
|
||||
htm`<a href="${'https'}://example.com/"${'foo'}/bar/${'baz'}?foo=bar&baz=${'egg'}">link</a>`,
|
||||
[
|
||||
createEntity('messageEntityTextUrl', 0, 4, {
|
||||
url: 'https://example.com/"foo/bar/baz?foo=bar&baz=egg',
|
||||
}),
|
||||
],
|
||||
'link',
|
||||
)
|
||||
test(
|
||||
// at the same time testing that non-quoted attributes work
|
||||
htm`<a href=tg://user?id=${1234567}&hash=${'aabbccddaabbccdd'}>user</a>`,
|
||||
[
|
||||
createEntity('inputMessageEntityMentionName', 0, 4, {
|
||||
userId: {
|
||||
_: 'inputUser',
|
||||
userId: 1234567,
|
||||
accessHash: Long.fromString('aabbccddaabbccdd', 16),
|
||||
},
|
||||
}),
|
||||
],
|
||||
'user',
|
||||
)
|
||||
test(
|
||||
htm`<tg-emoji id="${'123123123123'}">🚀</tg-emoji>`,
|
||||
[
|
||||
createEntity('messageEntityCustomEmoji', 0, 2, {
|
||||
documentId: Long.fromString('123123123123'),
|
||||
}),
|
||||
],
|
||||
'🚀',
|
||||
)
|
||||
})
|
||||
|
||||
it('should skip falsy values', () => {
|
||||
test(htm`some text ${null} some ${false} more text`, [], 'some text some more text')
|
||||
})
|
||||
|
|
|
@ -27,6 +27,8 @@ function parse(
|
|||
let plainText = ''
|
||||
let pendingText = ''
|
||||
|
||||
let isInsideAttrib = false
|
||||
|
||||
function processPendingText(tagEnd = false, keepWhitespace = false) {
|
||||
if (!pendingText.length) return
|
||||
|
||||
|
@ -212,8 +214,30 @@ function parse(
|
|||
ontext(data) {
|
||||
pendingText += data
|
||||
},
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
})
|
||||
|
||||
// a hack for interpolating inside attributes
|
||||
// instead of hacking into the parser itself (which would require a lot of work
|
||||
// and test coverage because of the number of edge cases), we'll just feed
|
||||
// an escaped version of the text right to the parser.
|
||||
// however, to do that we need to know if we are inside an attribute or not,
|
||||
// and htmlparser2 doesn't really expose that.
|
||||
// it only exposes .onattribute, which isn't really useful here, as
|
||||
// we want to know if we are mid-attribute or not
|
||||
const onattribname = parser.onattribname
|
||||
const onattribend = parser.onattribend
|
||||
|
||||
parser.onattribname = function (name) {
|
||||
onattribname.call(this, name)
|
||||
isInsideAttrib = true
|
||||
}
|
||||
|
||||
parser.onattribend = function (quote) {
|
||||
onattribend.call(this, quote)
|
||||
isInsideAttrib = false
|
||||
}
|
||||
|
||||
if (typeof strings === 'string') strings = [strings] as unknown as TemplateStringsArray
|
||||
|
||||
sub.forEach((it, idx) => {
|
||||
|
@ -221,6 +245,20 @@ function parse(
|
|||
|
||||
if (typeof it === 'boolean' || !it) return
|
||||
|
||||
if (isInsideAttrib) {
|
||||
let text: string
|
||||
|
||||
if (typeof it === 'string') text = it
|
||||
else if (typeof it === 'number') text = it.toString()
|
||||
else {
|
||||
// obviously we can't have entities inside attributes, so just use the text
|
||||
text = it.text
|
||||
}
|
||||
parser.write(escape(text, true))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (typeof it === 'string' || typeof it === 'number') {
|
||||
pendingText += it
|
||||
} else {
|
||||
|
|
Loading…
Reference in a new issue