Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ export function applyAuthorLinks(container: HTMLElement): Root[] {
return;
}

// Skip mentions inside code blocks or inline code
if (el.closest("code") || el.closest("pre")) return;

// Skip mentions inside archived tweet blocks
if (el.closest(".markdown-view")?.textContent?.includes("Archived Tweet from")) return;

Expand Down
6 changes: 6 additions & 0 deletions packages/render-helper/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# @ecency/render-helper

## 2.4.23

### Patch Changes

- Fix wrapping on render helper (#699)

## 2.4.22

### Patch Changes
Expand Down
12 changes: 11 additions & 1 deletion packages/render-helper/dist/browser/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/render-helper/dist/browser/index.js.map

Large diffs are not rendered by default.

12 changes: 11 additions & 1 deletion packages/render-helper/dist/node/index.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -1261,11 +1261,21 @@ function linkify(content, forApp) {
}

// src/methods/text.method.ts
function hasAncestor(node, tagNames) {
let current = node.parentNode;
while (current) {
if (tagNames.includes(current.nodeName.toLowerCase())) {
return true;
}
current = current.parentNode;
}
return false;
}
function text(node, forApp) {
if (!node || !node.parentNode) {
return;
}
if (["a", "code"].includes(node.parentNode.nodeName.toLowerCase())) {
if (hasAncestor(node, ["a", "code", "pre"])) {
return;
}
const nodeValue = node.nodeValue || "";
Expand Down
2 changes: 1 addition & 1 deletion packages/render-helper/dist/node/index.cjs.map

Large diffs are not rendered by default.

12 changes: 11 additions & 1 deletion packages/render-helper/dist/node/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1232,11 +1232,21 @@ function linkify(content, forApp) {
}

// src/methods/text.method.ts
function hasAncestor(node, tagNames) {
let current = node.parentNode;
while (current) {
if (tagNames.includes(current.nodeName.toLowerCase())) {
return true;
}
current = current.parentNode;
}
return false;
}
function text(node, forApp) {
if (!node || !node.parentNode) {
return;
}
if (["a", "code"].includes(node.parentNode.nodeName.toLowerCase())) {
if (hasAncestor(node, ["a", "code", "pre"])) {
return;
}
const nodeValue = node.nodeValue || "";
Expand Down
2 changes: 1 addition & 1 deletion packages/render-helper/dist/node/index.mjs.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/render-helper/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@ecency/render-helper",
"private": false,
"version": "2.4.22",
"version": "2.4.23",
"description": "Markdown+Html Render helper",
"repository": {
"type": "git",
Expand Down
58 changes: 58 additions & 0 deletions packages/render-helper/src/markdown-2-html.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1241,6 +1241,64 @@ describe('Markdown2Html', () => {
})
})

describe('Inline code should not linkify mentions', () => {
it('should not create author links inside backtick-wrapped text', () => {
const input = '`@aws-sdk/client-s3`'
const result = markdown2Html(input, false)
expect(result).toContain('<code>@aws-sdk/client-s3</code>')
expect(result).not.toContain('markdown-author-link')
expect(result).not.toContain('markdown-post-link')
})

it('should not create author links inside inline code in a sentence', () => {
const input = '~3MB (`@aws-sdk/client-s3` only)'
const result = markdown2Html(input, false)
expect(result).toContain('<code>@aws-sdk/client-s3</code>')
expect(result).not.toContain('markdown-author-link')
expect(result).not.toContain('markdown-post-link')
})

it('should not create author links inside backtick-wrapped username', () => {
const input = '`@hiveio/dhive`'
const result = markdown2Html(input, false)
expect(result).toContain('<code>@hiveio/dhive</code>')
expect(result).not.toContain('markdown-author-link')
expect(result).not.toContain('markdown-post-link')
})
Comment thread
coderabbitai[bot] marked this conversation as resolved.

it('should not create author links for simple mention inside backticks', () => {
const input = 'use `@scope` package'
const result = markdown2Html(input, false)
expect(result).toContain('<code>@scope</code>')
expect(result).not.toContain('markdown-author-link')
})

it('should not linkify mentions inside fenced code blocks', () => {
const input = '```\n@aws-sdk/client-s3\n```'
const result = markdown2Html(input, false)
// Code block wrapper must be present (content may be syntax-highlighted into spans)
expect(result).toContain('<pre><code>')
expect(result).toContain('</code></pre>')
expect(result).toMatch(/aws/)
expect(result).toMatch(/sdk/)
expect(result).not.toContain('markdown-author-link')
expect(result).not.toContain('markdown-post-link')
})
Comment thread
coderabbitai[bot] marked this conversation as resolved.

it('should not linkify hashtags inside inline code', () => {
const input = 'use `#hashtag` in code'
const result = markdown2Html(input, false)
expect(result).toContain('<code>#hashtag</code>')
expect(result).not.toContain('markdown-tag-link')
})

it('should still linkify mentions outside of code', () => {
const input = '@goodkarma is great'
const result = markdown2Html(input, false)
expect(result).toContain('markdown-author-link')
})
})

describe('Format handling (webp via server Accept header)', () => {
it('Should always render images with match format regardless of webp flag', () => {
const input = 'lorem ipsum https://images.ecency.com/foobarbaz.jpg dolor sit amet'
Expand Down
43 changes: 43 additions & 0 deletions packages/render-helper/src/methods/text.method.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -812,6 +812,49 @@ describe('text() method - Text Node Processing', () => {
expect(parent.textContent).toBe(originalText)
})

it('should skip processing when ancestor is <code> tag (nested)', () => {
const code = doc.createElement('code')
const em = doc.createElement('em')
doc.body?.appendChild(code)
code.appendChild(em)
const textNode = doc.createTextNode('@aws-sdk')
em.appendChild(textNode)

text(textNode as any, false)

// Should not process because ancestor is <code>
// Check no anchor was inserted (textContent alone won't catch a wrapped <a>)
expect(em.textContent).toBe('@aws-sdk')
expect(code.getElementsByTagName('a').length).toBe(0)
})

it('should skip processing when ancestor is <pre> tag', () => {
const pre = doc.createElement('pre')
const code = doc.createElement('code')
doc.body?.appendChild(pre)
pre.appendChild(code)
const textNode = doc.createTextNode('@hiveio #hashtag')
code.appendChild(textNode)

text(textNode as any, false)

// Should not process because ancestor is <pre>
expect(code.textContent).toBe('@hiveio #hashtag')
expect(pre.getElementsByTagName('a').length).toBe(0)
})

it('should skip processing when text is inside <pre> without <code>', () => {
const pre = doc.createElement('pre')
doc.body?.appendChild(pre)
const textNode = doc.createTextNode('@mention')
pre.appendChild(textNode)

text(textNode as any, false)

expect(pre.textContent).toBe('@mention')
expect(pre.getElementsByTagName('a').length).toBe(0)
})
Comment thread
coderabbitai[bot] marked this conversation as resolved.

it('should handle empty text nodes', () => {
const parent = doc.createElement('p')
doc.body?.appendChild(parent)
Expand Down
15 changes: 13 additions & 2 deletions packages/render-helper/src/methods/text.method.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,24 @@ import { proxifyImageSrc } from '../proxify-image-src'
import { linkify } from './linkify.method'
import {createImageHTML} from "./img.method";

function hasAncestor(node: Node, tagNames: string[]): boolean {
let current = node.parentNode
while (current) {
if (tagNames.includes(current.nodeName.toLowerCase())) {
return true
}
current = current.parentNode
}
return false
}

export function text(node: HTMLElement | null, forApp: boolean): void {
if (!node || !node.parentNode) {
return
}

// Case-insensitive check
if (['a', 'code'].includes(node.parentNode.nodeName.toLowerCase())) {
// Skip text nodes inside links, inline code, or code blocks (check all ancestors)
if (hasAncestor(node, ['a', 'code', 'pre'])) {
return
}

Expand Down