diff --git a/CHANGELOG.md b/CHANGELOG.md index e4453cd..9eb2648 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,8 @@ 20 lines of 40000 tabs under 80000 nested items took 1988 ms. - `losslessMarkdownToAdf` takes time linear in the markdown when an inline directive's `[content]` holds many unclosed backtick runs: 640000 characters of them took one second, now 100 ms. +- Every reader leaves `[text]` and `[text][]` literal when the text runs over 999 characters, where + it read them as a link to the matching definition: CommonMark caps a link label at 999 characters. - The README compares the package with seven other ADF converters, each checked against its published version. diff --git a/src/markdown/parse/inline-content.ts b/src/markdown/parse/inline-content.ts index aef86c0..e70e0b7 100644 --- a/src/markdown/parse/inline-content.ts +++ b/src/markdown/parse/inline-content.ts @@ -432,8 +432,9 @@ function resolveTarget(scan: Scan, start: number, index: number): { definition: } const label = scan.source.charAt(after) === '[' ? readLabel(scan.source, after) : undefined // CommonMark: a label holds at most 999 characters, so longer link text names no definition. - if (label === undefined && index - start > 999) return undefined - const name = label === undefined || label.value === '' ? scan.source.slice(start, index) : label.value + const labelIsText = label === undefined || label.value === '' + if (labelIsText && index - start > 999) return undefined + const name = labelIsText ? scan.source.slice(start, index) : label.value const definition = scan.definitions.get(normalizeLabel(name)) if (definition === undefined) return undefined return { definition, length: label?.length ?? 0 } diff --git a/src/markdown/parse/markdown-to-adf.test.ts b/src/markdown/parse/markdown-to-adf.test.ts index 9273bc1..05e555d 100644 --- a/src/markdown/parse/markdown-to-adf.test.ts +++ b/src/markdown/parse/markdown-to-adf.test.ts @@ -1076,6 +1076,17 @@ test('reads the reference links a definition resolves, and leaves the rest liter assert.deepEqual(content(losslessMarkdownToAdf('[][]\n')), [paragraph('[][]')]) }) +test('reads link text past 999 characters as no label, in every reader', () => { + const longest = `foo${' '.repeat(993)}bar` + const tooLong = `foo${' '.repeat(1000)}bar` + for (const read of [commonMarkToAdf, losslessMarkdownToAdf, portableMarkdownToAdf]) { + assert.deepEqual(content(read(`[${longest}]\n\n[foo bar]: /u\n`)), [{ content: [marked(longest, link('/u'))], type: 'paragraph' }]) + assert.deepEqual(content(read(`[${longest}][]\n\n[foo bar]: /u\n`)), [{ content: [marked(longest, link('/u'))], type: 'paragraph' }]) + assert.deepEqual(content(read(`[${tooLong}]\n\n[foo bar]: /u\n`)), [paragraph(`[${tooLong}]`)]) + assert.deepEqual(content(read(`[${tooLong}][]\n\n[foo bar]: /u\n`)), [paragraph(`[${tooLong}][]`)]) + } +}) + test('reads an autolink, the email form as the mailto link it means', () => { assert.deepEqual(content(losslessMarkdownToAdf('\n')), [ { content: [marked('https://example.com/', link('https://example.com/'))], type: 'paragraph' },