Skip to content

Commit ec5df2a

Browse files
committed
Reduce number of nodes created when using linkify
1 parent 960789b commit ec5df2a

File tree

2 files changed

+63
-33
lines changed

2 files changed

+63
-33
lines changed

__tests__/index.spec.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,34 @@ describe("Ansi", () => {
135135
);
136136
});
137137

138+
test("can linkify multiple links", () => {
139+
const el = shallow(
140+
React.createElement(
141+
Ansi,
142+
{ linkify: true },
143+
"this is a link: www.google.com and this is a second link: www.microsoft.com"
144+
)
145+
);
146+
expect(el).not.toBeNull();
147+
expect(el.text()).toBe("this is a link: www.google.com and this is a second link: www.microsoft.com");
148+
expect(el.html()).toBe(
149+
'<code><span>this is a link: <a href=\"http://www.google.com\" target=\"_blank\">www.google.com</a> and this is a second link: <a href=\"http://www.microsoft.com\" target=\"_blank\">www.microsoft.com</a></span></code>'
150+
);
151+
});
152+
153+
test("creates a minimal number of nodes when using linkify", () => {
154+
const el = shallow(
155+
React.createElement(
156+
Ansi,
157+
{ linkify: true },
158+
"this is a link: www.google.com and this is text after"
159+
)
160+
);
161+
expect(el).not.toBeNull();
162+
expect(el.text()).toBe("this is a link: www.google.com and this is text after");
163+
expect(el.childAt(0).children()).toHaveLength(3);
164+
});
165+
138166
describe("useClasses options", () => {
139167
test("can add the font color class", () => {
140168
const el = shallow(

src/index.ts

Lines changed: 35 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ import Anser, { AnserJsonEntry } from "anser";
22
import { escapeCarriageReturn } from "escape-carriage";
33
import * as React from "react";
44

5-
const LINK_REGEX = /^(https?:\/\/(?:www\.|(?!www))[^\s.]+\.[^\s]{2,}|www\.[^\s]+\.[^\s]{2,})$/;
6-
75
/**
86
* Converts ANSI strings into JSON output.
97
* @name ansiToJSON
@@ -102,37 +100,41 @@ function convertBundleIntoReact(
102100
);
103101
}
104102

105-
const content = bundle.content
106-
.split(/(\s+)/)
107-
.reduce((words: React.ReactNode[], word: string, index: number) => {
108-
// If this is a separator, re-add the space removed from split.
109-
if (index % 2 === 1) {
110-
words.push(word);
111-
return words;
112-
}
113-
114-
// If this isn't a link, just return the word as-is.
115-
if (!LINK_REGEX.test(word)) {
116-
words.push(word);
117-
return words;
118-
}
119-
120-
// Make sure the href we generate from the link is fully qualified. We assume http
121-
// if it starts with a www because many sites don't support https
122-
const href = word.startsWith("www.") ? `http://${word}` : word;
123-
words.push(
124-
React.createElement(
125-
"a",
126-
{
127-
key: index,
128-
href,
129-
target: "_blank"
130-
},
131-
`${word}`
132-
)
133-
);
134-
return words;
135-
}, [] as React.ReactNode[]);
103+
const content: React.ReactNode[] = [];
104+
const linkRegex = /(\s+|^)(https?:\/\/(?:www\.|(?!www))[^\s.]+\.[^\s]{2,}|www\.[^\s]+\.[^\s]{2,})(\s+|$)/g;
105+
106+
let index = 0;
107+
let match: RegExpExecArray | null;
108+
while ((match = linkRegex.exec(bundle.content)) !== null) {
109+
const startIndex = match.index + match[1].length;
110+
if (startIndex > index) {
111+
content.push(bundle.content.substring(index, startIndex));
112+
}
113+
114+
const link = match[2];
115+
// Make sure the href we generate from the link is fully qualified. We assume http
116+
// if it starts with a www because many sites don't support https
117+
const href = link.startsWith("www.") ? `http://${link}` : link;
118+
content.push(
119+
React.createElement(
120+
"a",
121+
{
122+
key: index,
123+
href,
124+
target: "_blank"
125+
},
126+
`${link}`
127+
)
128+
);
129+
130+
const endIndex = linkRegex.lastIndex - match[3].length;
131+
index = endIndex;
132+
}
133+
134+
if (index < bundle.content.length) {
135+
content.push(bundle.content.substring(index));
136+
}
137+
136138
return React.createElement("span", { style, key, className }, content);
137139
}
138140

0 commit comments

Comments
 (0)