From fc9bb7cec159f92b77af45d352378c50c952856d Mon Sep 17 00:00:00 2001 From: Katsute <58778985+Katsute@users.noreply.github.com> Date: Sat, 9 Sep 2023 05:00:25 -0400 Subject: [PATCH] Skip truncated links (#40) Co-authored-by: Federico Brigante --- index.js | 16 +++++++++++++--- test.js | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index af4e5fe..98702cc 100644 --- a/index.js +++ b/index.js @@ -19,12 +19,22 @@ const linkify = (href, options) => createHtmlElement({ // Get DOM node from HTML const domify = html => document.createRange().createContextualFragment(html); -const getAsString = (string, options) => string.replace(urlRegex(), match => linkify(match, options)); +const isTruncated = (url, peek) => + url.endsWith('...') // `...` is a matched by the URL regex + || peek.startsWith('…'); // `…` can follow the match + +const getAsString = (string, options) => string.replace(urlRegex(), (url, _, offset) => + (isTruncated(url, string.charAt(offset + url.length))) + ? url // Don't linkify truncated URLs + : linkify(url, options)); const getAsDocumentFragment = (string, options) => { const fragment = document.createDocumentFragment(); - for (const [index, text] of Object.entries(string.split(urlRegex()))) { - if (index % 2) { // URLs are always in odd positions + const parts = string.split(urlRegex()); + + for (const [index, text] of parts.entries()) { + // URLs are always in odd positions + if (index % 2 && !isTruncated(text, parts[index + 1])) { fragment.append(domify(linkify(text, options))); } else if (text.length > 0) { fragment.append(text); diff --git a/test.js b/test.js index 50ceee4..93ef67c 100644 --- a/test.js +++ b/test.js @@ -147,3 +147,41 @@ test('supports localhost URLs', t => { t.is(linkifyUrls('https://localhost'), 'https://localhost'); t.is(linkifyUrls('https://localhost/foo/bar'), 'https://localhost/foo/bar'); }); + +test('skips truncated URLs', t => { + t.is(linkifyUrls('https://github.com/sindresorhus/linkify-…'), 'https://github.com/sindresorhus/linkify-…'); + t.is(linkifyUrls('https://github.com/sindresorhus/linkify-… and https://github.com/sindresorhus/linkify-…'), 'https://github.com/sindresorhus/linkify-… and https://github.com/sindresorhus/linkify-…'); + t.is(linkifyUrls('https://github.com/sindresorhus/linkify-urls and more…'), 'https://github.com/sindresorhus/linkify-urls and more…'); + + t.is(linkifyUrls('https://github.com/sindresorhus/linkify-...'), 'https://github.com/sindresorhus/linkify-...'); + t.is(linkifyUrls('https://github.com/sindresorhus/linkify-... and https://github.com/sindresorhus/linkify-...'), 'https://github.com/sindresorhus/linkify-... and https://github.com/sindresorhus/linkify-...'); + t.is(linkifyUrls('https://github.com/sindresorhus/linkify-urls and more...'), 'https://github.com/sindresorhus/linkify-urls and more...'); +}); + +test('skips truncated URLs (DocumentFragment)', t => { + t.is( + html(linkifyUrls('See https://github.com/sindresorhus/linkify-urls and https://github.com/sindresorhus/linkify-…', { + type: 'dom', + })), + html(domify('See https://github.com/sindresorhus/linkify-urls and https://github.com/sindresorhus/linkify-…')), + ); + t.is( + html(linkifyUrls('See https://github.com/sindresorhus/linkify-urls… and https://github.com/sindresorhus/linkify-…', { + type: 'dom', + })), + html(domify('See https://github.com/sindresorhus/linkify-urls… and https://github.com/sindresorhus/linkify-…')), + ); + + t.is( + html(linkifyUrls('See https://github.com/sindresorhus/linkify-urls and https://github.com/sindresorhus/linkify-...', { + type: 'dom', + })), + html(domify('See https://github.com/sindresorhus/linkify-urls and https://github.com/sindresorhus/linkify-...')), + ); + t.is( + html(linkifyUrls('See https://github.com/sindresorhus/linkify-... and https://github.com/sindresorhus/linkify-...', { + type: 'dom', + })), + html(domify('See https://github.com/sindresorhus/linkify-... and https://github.com/sindresorhus/linkify-...')), + ); +});