Merge pull request #6912 from redpersongpt/codex/email-linkify-single-pass

fix(email): linkify URLs and addresses without rewriting anchors
This commit is contained in:
Alexandre Teixeira
2026-10-30 19:46:54 +00:00
committed by GitHub
2 changed files with 71 additions and 10 deletions
+18 -10
View File
@@ -73,16 +73,24 @@ function _isDangerousSrcset(value) {
// Escape + linkify URLs and email addresses. Returns innerHTML-safe markup.
export function _escLinkify(text) {
const escaped = _esc(text);
// URLs: http(s)://... or www....
const urlRe = /\b((?:https?:\/\/|www\.)[^\s<>"']+[^\s<>"'.,;:!?)\]])/g;
const mailRe = /\b([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,})\b/g;
return escaped
.replace(urlRe, (m) => {
const href = m.startsWith('www.') ? `https://${m}` : m;
return `<a href="${_attrEsc(href)}" target="_blank" rel="noopener noreferrer">${m}</a>`;
})
.replace(mailRe, (m) => `<a href="${_attrEsc(`mailto:${m}`)}">${m}</a>`);
const raw = String(text || '');
// Match raw text once so generated anchors are never linkified again.
const tokenRe = /\b((?:https?:\/\/|www\.)[^\s<>"']+[^\s<>"'.,;:!?)\]])|\b([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,})\b/g;
const parts = [];
let offset = 0;
for (const match of raw.matchAll(tokenRe)) {
parts.push(_esc(raw.slice(offset, match.index)));
const token = match[0];
const isUrl = !!match[1];
const href = isUrl
? (token.startsWith('www.') ? `https://${token}` : token)
: `mailto:${token}`;
const attrs = isUrl ? ' target="_blank" rel="noopener noreferrer"' : '';
parts.push(`<a href="${_attrEsc(_esc(href))}"${attrs}>${_esc(token)}</a>`);
offset = match.index + token.length;
}
parts.push(_esc(raw.slice(offset)));
return parts.join('');
}
// Pull display name out of "Name <email@x>"; fallback to local-part of