mirror of
https://github.com/zhigang1992/mtcute.git
synced 2026-04-29 04:35:08 +08:00
fix(markdown)!: greacefully handle malformed input
This commit is contained in:
@@ -308,17 +308,19 @@ function parse(
|
||||
pos += 1
|
||||
|
||||
if (pos > text.length) {
|
||||
throw new Error('Malformed PRE entity, expected LF after ```')
|
||||
// malformed pre entity, treat as plain text
|
||||
result += '```'
|
||||
feed(language)
|
||||
} else {
|
||||
if (!('pre' in stacks)) stacks.pre = []
|
||||
stacks.pre.push({
|
||||
_: 'messageEntityPre',
|
||||
offset: result.length,
|
||||
length: 0,
|
||||
language,
|
||||
})
|
||||
insidePre = true
|
||||
}
|
||||
|
||||
if (!('pre' in stacks)) stacks.pre = []
|
||||
stacks.pre.push({
|
||||
_: 'messageEntityPre',
|
||||
offset: result.length,
|
||||
length: 0,
|
||||
language,
|
||||
})
|
||||
insidePre = true
|
||||
} else {
|
||||
pos += 1
|
||||
if (!('code' in stacks)) stacks.code = []
|
||||
@@ -442,9 +444,55 @@ function parse(
|
||||
|
||||
feed(strings[strings.length - 1])
|
||||
|
||||
function adjustOffsets(from: number, by: number): void {
|
||||
for (const ent of entities) {
|
||||
if (ent.offset >= from) ent.offset += by
|
||||
}
|
||||
for (const stack of Object.values(stacks)) {
|
||||
for (const ent of stack) {
|
||||
if (ent.offset >= from) ent.offset += by
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const [name, stack] of Object.entries(stacks)) {
|
||||
if (stack.length > 1) {
|
||||
// todo: is this even possible?
|
||||
throw new Error(`Malformed ${name} entity`)
|
||||
}
|
||||
|
||||
if (stack.length) {
|
||||
throw new Error(`Unterminated ${name} entity`)
|
||||
// unterminated entity
|
||||
switch (name) {
|
||||
case 'link': {
|
||||
const startOffset = stack.pop()!.offset
|
||||
insideLink = false
|
||||
insideLinkUrl = false
|
||||
const url = pendingLinkUrl
|
||||
pendingLinkUrl = ''
|
||||
result = `${result.substring(0, startOffset)}[${result.substring(startOffset)}](`
|
||||
adjustOffsets(startOffset, 1)
|
||||
feed(url)
|
||||
break
|
||||
}
|
||||
default: {
|
||||
const startOffset = stack.pop()!.offset
|
||||
const tag = {
|
||||
Bold: TAG_BOLD,
|
||||
Italic: TAG_ITALIC,
|
||||
Underline: TAG_UNDERLINE,
|
||||
Strike: TAG_STRIKE,
|
||||
Spoiler: TAG_SPOILER,
|
||||
code: TAG_CODE,
|
||||
}[name]
|
||||
if (!tag) throw new Error(`invalid tag ${name}`) // should never happen
|
||||
const remaining = result.substring(startOffset)
|
||||
result = `${result.substring(0, startOffset)}${tag}`
|
||||
adjustOffsets(startOffset, tag.length)
|
||||
feed(remaining)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -454,9 +502,6 @@ function parse(
|
||||
}
|
||||
}
|
||||
|
||||
// typedoc doesn't support this yet, so we'll have to do it manually
|
||||
// https://github.com/TypeStrong/typedoc/issues/2436
|
||||
|
||||
export const md: {
|
||||
/**
|
||||
* Tagged template based Markdown-to-entities parser function
|
||||
|
||||
@@ -481,20 +481,42 @@ describe('MarkdownMessageEntityParser', () => {
|
||||
})
|
||||
|
||||
describe('malformed input', () => {
|
||||
const testThrows = (input: string) => expect(() => md_(input)).throws(Error)
|
||||
it('should treat malformed links as plain text', () => {
|
||||
test(
|
||||
'plain [link](https://google.com but unclosed',
|
||||
[],
|
||||
'plain [link](https://google.com but unclosed',
|
||||
)
|
||||
|
||||
it('should throw an error on malformed links', () => {
|
||||
testThrows('plain [link](https://google.com but unclosed')
|
||||
test(
|
||||
'plain [**bold link**](https://google.com but __unclosed__',
|
||||
[createEntity('messageEntityBold', 7, 9), createEntity('messageEntityItalic', 41, 8)],
|
||||
'plain [bold link](https://google.com but unclosed',
|
||||
)
|
||||
})
|
||||
|
||||
it('should throw an error on malformed pres', () => {
|
||||
testThrows('plain ```pre without linebreaks```')
|
||||
testThrows('plain ``` pre without linebreaks but with spaces instead ```')
|
||||
it('should ignore malformed pres', () => {
|
||||
test('plain ```pre without linebreaks```', [], 'plain ```pre without linebreaks```')
|
||||
test('plain ``` pre without linebreaks but with spaces instead ```', [], 'plain ``` pre without linebreaks but with spaces instead ```')
|
||||
})
|
||||
|
||||
it('should throw an error on unterminated entity', () => {
|
||||
testThrows('plain **bold but unclosed')
|
||||
testThrows('plain **bold and __also italic but unclosed')
|
||||
it('should ignore unterminated entities', () => {
|
||||
test('plain **bold but unclosed', [], 'plain **bold but unclosed')
|
||||
test('meow**', [], 'meow**')
|
||||
test('meow**__', [], 'meow**__')
|
||||
test('meow`woof', [], 'meow`woof')
|
||||
test('meow```woof', [], 'meow```woof')
|
||||
test(
|
||||
'meow```woof **bold**',
|
||||
[createEntity('messageEntityBold', 12, 4)],
|
||||
'meow```woof bold',
|
||||
)
|
||||
test('plain **bold and __also italic but unclosed', [], 'plain **bold and __also italic but unclosed')
|
||||
test(
|
||||
'plain **bold and __italic__',
|
||||
[createEntity('messageEntityItalic', 17, 6)],
|
||||
'plain **bold and italic',
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user