Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/serializers/html/html.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,12 @@ Answer: [Doist Frontend](channel://190200)`),
)
})

test('angle-bracket URLs are preserved as text', () => {
expect(htmlSerializer.serialize('<https://duckduckgo.com>')).toBe(
'<p>&lt;https://duckduckgo.com></p>',
)
})

test('styled links HTML output is correct', () => {
expect(htmlSerializer.serialize(MARKDOWN_INPUT_STYLED_LINKS)).toBe(
'<p>I love supporting the <strong><a href="https://eff.org">EFF</a></strong>.<br>I love supporting the <strong><a href="https://eff.org">https://eff.org</a></strong>.<br>This is the <em><a href="https://www.markdownguide.org">Markdown Guide</a></em>.<br>This is the <em><a href="https://www.markdownguide.org">https://www.markdownguide.org</a></em>.<br>See the section on <a href="#code"><code>code</code></a>.</p>',
Expand Down Expand Up @@ -809,6 +815,12 @@ My favorite search engine is [Duck Duck Go](https://duckduckgo.com "The best sea
My favorite search engine is https://duckduckgo.com.</p>`)
})

test('angle-bracket URLs are preserved as text without the link extension', () => {
expect(htmlSerializer.serialize('<https://duckduckgo.com>')).toBe(
'<p>&lt;https://duckduckgo.com></p>',
)
})

test('styled links HTML output is preserved', () => {
expect(htmlSerializer.serialize(MARKDOWN_INPUT_STYLED_LINKS))
.toBe(`<p>I love supporting the **[EFF](https://eff.org)**.
Expand Down
4 changes: 4 additions & 0 deletions src/serializers/html/html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { rehypeTable } from './plugins/rehype-table'
import { rehypeTaskList } from './plugins/rehype-task-list'
import { remarkAutolinkLiteral } from './plugins/remark-autolink-literal'
import { remarkDisableConstructs } from './plugins/remark-disable-constructs'
import { remarkIgnoreAngleBracketAutolinks } from './plugins/remark-ignore-angle-bracket-autolinks'
import { remarkStrikethrough } from './plugins/remark-strikethrough'
import { remarkTable } from './plugins/remark-table'

Expand Down Expand Up @@ -101,6 +102,9 @@ function createHTMLSerializer(schema: Schema): HTMLSerializerReturnType {
// supported extensions that are enabled in the editor schema
unifiedProcessor.use(remarkDisableConstructs, schema)

// Ignore angle-bracket autolinks because they are not a supported rich-text Markdown shortcut
unifiedProcessor.use(remarkIgnoreAngleBracketAutolinks)

// Configure the unified processor to use a third-party plugin to turn soft line endings into
// hard breaks (i.e. `<br>`), which will display user content closer to how it was authored
// (although not CommonMark compliant, this resembles the behaviour we always supported)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { visit } from 'unist-util-visit'

import type { Transformer } from 'unified'
import type { Parent } from 'unist'

/**
* Treats angle-bracket HTTP(S) autolinks as plain text because they are not a supported rich-text
* Markdown shortcut.
*/
function remarkIgnoreAngleBracketAutolinks(): Transformer {
Comment thread
rfgamaral marked this conversation as resolved.
Outdated
return (tree, file) => {
visit(tree as Parent, (node, index, parent) => {
const startOffset = node.position?.start.offset
const endOffset = node.position?.end.offset

if (
node.type !== 'link' ||
typeof startOffset !== 'number' ||
typeof endOffset !== 'number' ||
typeof index !== 'number' ||
!parent
) {
return
}

const source = String(file.value).slice(startOffset, endOffset)

if (/^<https?:\/\/\S+>$/i.test(source)) {
const textNode = { type: 'text', value: source }
parent.children[index] = textNode
}
})
}
}

export { remarkIgnoreAngleBracketAutolinks }
Loading