function textNode(text) { const node = new commonmark.Node("text", undefined); node.literal = text; return node; } function linkNode(text, url) { const urlNode = new commonmark.Node("link", undefined); urlNode.destination = url; urlNode.appendChild(textNode(text)); return urlNode; } function splitMatches(text, regexp) { // Regexp must be sticky. regexp = new RegExp(regexp, "gm"); let i = 0; const result = []; let match = regexp.exec(text); while (match) { const matchText = match[0]; if (match.index > i) { result.push([text.substring(i, match.index), false]); } result.push([matchText, true]); i = match.index + matchText.length; match = regexp.exec(text); } if (i < text.length) { result.push([text.substring(i, text.length), false]); } return result; } const urlRegexp = new RegExp("https?://[^ ]+[^ .,]"); function splitURLs(textNodes) { const text = textNodes.map(n => n.literal).join(""); const parts = splitMatches(text, urlRegexp); return parts.map(part => { if (part[1]) { return linkNode(part[0], part[0]); } else { return textNode(part[0]); } }); } export function transform(parsed) { const walker = parsed.walker(); let event; let nodes = []; while ((event = walker.next())) { const node = event.node; if (event.entering && node.type === "text") { nodes.push(node); } else { if (nodes.length > 0) { splitURLs(nodes) .reverse() .forEach(newNode => { nodes[0].insertAfter(newNode); }); nodes.forEach(n => n.unlink()); nodes = []; } } } if (nodes.length > 0) { splitURLs(nodes) .reverse() .forEach(newNode => { nodes[0].insertAfter(newNode); }); nodes.forEach(n => n.unlink()); } return parsed; }