|
1 |
| -const URL_REGEX = /https?:\/\/[^\s]+/g |
2 |
| -const LINK_REGEX = /<a\s+([^>]*href\s*=\s*[^>]*)>/gi |
| 1 | +// Combined regex that matches either existing anchor tags OR standalone URLs |
| 2 | +const COMBINED_REGEX = |
| 3 | + /(<a\s+[^>]*href\s*=\s*[^>]*>.*?<\/a>)|(https?:\/\/[^\s]+)/gi |
3 | 4 |
|
4 | 5 | export function formatDescription(text: string): string {
|
5 |
| - // we coerce all existing anchor tags to have target="_blank" rel="noopener noreferrer" and typography-link class |
6 |
| - const result = text.replace(LINK_REGEX, (_, attributes) => { |
7 |
| - let attrs = attributes |
8 |
| - |
9 |
| - if (!attrs.includes("target=")) { |
10 |
| - attrs += ' target="_blank"' |
11 |
| - } |
12 |
| - |
13 |
| - if (!attrs.includes("rel=")) { |
14 |
| - attrs += ' rel="noopener noreferrer"' |
15 |
| - } |
16 |
| - |
17 |
| - if (!attrs.includes("class=")) { |
18 |
| - attrs += ' class="typography-link"' |
19 |
| - } else if (!attrs.includes("typography-link")) { |
20 |
| - attrs = attrs.replace( |
21 |
| - /class\s*=\s*["']([^"']*)/gi, |
22 |
| - 'class="$1 typography-link', |
| 6 | + return text.replace(COMBINED_REGEX, (match, anchorTag, standaloneUrl) => { |
| 7 | + if (anchorTag) { |
| 8 | + // Handle existing anchor tag |
| 9 | + const linkMatch = anchorTag.match( |
| 10 | + /<a\s+([^>]*href\s*=\s*[^>]*)>(.*?)<\/a>/i, |
| 11 | + ) |
| 12 | + if (!linkMatch) return anchorTag |
| 13 | + |
| 14 | + const [, attributes, content] = linkMatch |
| 15 | + let attrs = attributes |
| 16 | + |
| 17 | + if (!attrs.includes("rel=")) { |
| 18 | + attrs += ' rel="noopener noreferrer"' |
| 19 | + } |
| 20 | + |
| 21 | + if (!attrs.includes("target=")) { |
| 22 | + attrs += ' target="_blank"' |
| 23 | + } |
| 24 | + |
| 25 | + if (!attrs.includes("class=")) { |
| 26 | + attrs += ' class=" typography-link"' |
| 27 | + } else if (!attrs.includes("typography-link")) { |
| 28 | + attrs = attrs.replace( |
| 29 | + /class\s*=\s*["']([^"']*)/gi, |
| 30 | + 'class="$1 typography-link', |
| 31 | + ) |
| 32 | + } |
| 33 | + |
| 34 | + // Format URL content to show just domain |
| 35 | + const urlContent = content.replace( |
| 36 | + /https?:\/\/[^\s]+/g, |
| 37 | + (url: string) => { |
| 38 | + return url.replace(/^https?:\/\//, "") |
| 39 | + }, |
23 | 40 | )
|
24 |
| - } |
25 |
| - |
26 |
| - return `<a ${attrs}>` |
27 |
| - }) |
28 |
| - |
29 |
| - // then we format plain URLs that are not already inside an anchor tag |
30 |
| - return result.replace(URL_REGEX, (url, offset) => { |
31 |
| - const beforeUrl = result.slice(0, offset) |
32 |
| - const afterUrl = result.slice(offset + url.length) |
33 |
| - |
34 |
| - const lastOpenTag = beforeUrl.lastIndexOf("<") |
35 |
| - const lastCloseTag = beforeUrl.lastIndexOf(">") |
36 |
| - const nextCloseTag = afterUrl.indexOf(">") |
37 | 41 |
|
38 |
| - if (lastOpenTag > lastCloseTag && nextCloseTag !== -1) { |
39 |
| - return url |
| 42 | + return `<a ${attrs}>${urlContent}</a>` |
| 43 | + } else if (standaloneUrl) { |
| 44 | + // Handle standalone URL |
| 45 | + const displayUrl = standaloneUrl.replace(/^https?:\/\//, "") |
| 46 | + return `<a href="${standaloneUrl}" target="_blank" rel="noopener noreferrer" class="typography-link">${displayUrl}</a>` |
40 | 47 | }
|
41 | 48 |
|
42 |
| - const displayUrl = url.replace(/^https?:\/\//, "") |
43 |
| - return `<a href="${url}" target="_blank" rel="noopener noreferrer" class="typography-link">${displayUrl}</a>` |
| 49 | + return match |
44 | 50 | })
|
45 | 51 | }
|
0 commit comments