Skip to content

Commit ed7419b

Browse files
authored
Merge pull request #8465 from nextcloud-libraries/backport/8449/stable8
2 parents df74096 + 806dad4 commit ed7419b

7 files changed

Lines changed: 64 additions & 16 deletions

File tree

package-lock.json

Lines changed: 5 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@
133133
"tributejs": "^5.1.3",
134134
"unified": "^11.0.1",
135135
"unist-builder": "^4.0.0",
136-
"unist-util-visit": "^5.1.0",
136+
"unist-util-visit-parents": "^6.0.2",
137137
"vue": "^2.7.16",
138138
"vue-color": "^2.8.1",
139139
"vue-frag": "^1.4.3",

src/components/NcRichText/autolink.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import { getBaseUrl, getRootUrl } from '@nextcloud/router'
77
import { u } from 'unist-builder'
8-
import { SKIP, visit } from 'unist-util-visit'
8+
import { SKIP, visitParents } from 'unist-util-visit-parents'
99
import { logger } from '../../utils/logger.ts'
1010
import { URL_PATTERN_AUTOLINK } from './helpers.js'
1111

@@ -45,7 +45,15 @@ export function remarkAutolink({ autolink, useMarkdown, useExtendedMarkdown }) {
4545
return
4646
}
4747

48-
visit(tree, (node) => node.type === 'text', (node, index, parent) => {
48+
visitParents(tree, (node) => node.type === 'text', (node, ancestors) => {
49+
// Do not autolink text already inside a link node
50+
if (ancestors.some((ancestor) => ancestor.type === 'link' || ancestor.type === 'linkReference')) {
51+
return
52+
}
53+
54+
const parent = ancestors.at(-1)
55+
const index = parent.children.indexOf(node) ?? 0
56+
4957
let parsed = parseUrl(node.value)
5058
parsed = parsed.map((n) => {
5159
if (typeof n === 'string') {

src/components/NcRichText/placeholder.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,25 @@
44
*/
55

66
import { u } from 'unist-builder'
7-
import { visit } from 'unist-util-visit'
7+
import { visitParents } from 'unist-util-visit-parents'
88
import { parseUrl } from './autolink.js'
99

1010
/**
1111
* Remark plugin for handling placeholders
1212
*/
1313
export function remarkPlaceholder() {
1414
return function(ast) {
15-
visit(ast, (node) => node.type === 'text', visitor)
15+
visitParents(ast, (node) => node.type === 'text', visitor)
1616

1717
/**
1818
*
1919
* @param {object} node The node
20-
* @param {number} index The index of the node
21-
* @param {object} parent The parent node
20+
* @param {array} ancestors The parent nodes
2221
*/
23-
function visitor(node, index, parent) {
22+
function visitor(node, ancestors) {
23+
const parent = ancestors.at(-1)
24+
const index = parent.children.indexOf(node)
25+
2426
const placeholders = node.value.split(/(\{[a-z\-_.0-9]+\})/ig)
2527
.map((entry) => {
2628
const matches = entry.match(/^\{([a-z\-_.0-9]+)\}$/i)

src/components/NcRichText/remarkStripCode.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import type { Plugin } from 'unified'
77
import type { Node, Parent } from 'unist'
88
import type { TextNode } from './helpers.ts'
99

10-
import { SKIP, visit } from 'unist-util-visit'
10+
import { SKIP, visitParents } from 'unist-util-visit-parents'
1111

1212
/**
1313
* Check if the given node is a literal and specifically a fenced node (inline code or code block)
@@ -20,7 +20,10 @@ function isCodeNode(node: Node): node is TextNode {
2020

2121
export const remarkStripCode: Plugin = function() {
2222
return function(tree: Node) {
23-
visit(tree, isCodeNode, (node: TextNode, index?: number, parent?: Parent) => {
23+
visitParents(tree, isCodeNode, (node: TextNode, ancestors: Parent[]) => {
24+
const parent = ancestors.at(-1)
25+
const index = parent!.children.indexOf(node)
26+
2427
parent!.children.splice(index!, 1, {
2528
...node,
2629
value: '',

src/components/NcRichText/remarkUnescape.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,17 @@
33
* SPDX-License-Identifier: AGPL-3.0-or-later
44
*/
55

6-
import { SKIP, visit } from 'unist-util-visit'
6+
import { SKIP, visitParents } from 'unist-util-visit-parents'
77

88
/**
99
*
1010
*/
1111
export function remarkUnescape() {
1212
return function(tree) {
13-
visit(tree, (node) => ['text', 'code', 'inlineCode'].includes(node.type), (node, index, parent) => {
13+
visitParents(tree, (node) => ['text', 'code', 'inlineCode'].includes(node.type), (node, ancestors) => {
14+
const parent = ancestors.at(-1)
15+
const index = parent.children.indexOf(node)
16+
1417
parent.children.splice(index, 1, {
1518
...node,
1619
value: node.value.replace(/&lt;/gmi, '<').replace(/&gt;/gmi, '>'),

tests/unit/components/NcRichText/NcRichText.spec.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,37 @@ describe('NcRichText', () => {
193193
expect(wrapper.find('em').text()).toEqual('to')
194194
})
195195

196+
it('does not autolink markdown link text that is already inside a link', async () => {
197+
const wrapper = mount(NcRichText, {
198+
propsData: {
199+
text: '[https://example-nested.org](https://example.com)',
200+
autolink: true,
201+
useMarkdown: true,
202+
},
203+
})
204+
205+
const links = wrapper.findAll('a')
206+
expect(links).toHaveLength(1)
207+
expect(links.at(0).attributes('href')).toEqual('https://example.com')
208+
expect(links.at(0).text()).toEqual('https://example-nested.org')
209+
})
210+
211+
it('does not autolink deeply nested markdown link text that is already inside a link', async () => {
212+
const wrapper = mount(NcRichText, {
213+
propsData: {
214+
text: '[**https://example-nested.org**](https://example.com)',
215+
autolink: true,
216+
useMarkdown: true,
217+
},
218+
})
219+
220+
const links = wrapper.findAll('a')
221+
expect(links).toHaveLength(1)
222+
expect(links.at(0).attributes('href')).toEqual('https://example.com')
223+
expect(links.at(0).text()).toEqual('https://example-nested.org')
224+
expect(wrapper.find('strong').text()).toEqual('https://example-nested.org')
225+
})
226+
196227
it('formats markdown is disabled', async () => {
197228
const wrapper = mount(NcRichText, {
198229
propsData: {

0 commit comments

Comments
 (0)