Skip to content

Commit

Permalink
feat: add full support for fuzzy linking
Browse files Browse the repository at this point in the history
this PR adds full support for fuzzy links. to keep backward
compat, the old linkify is still kept.
  • Loading branch information
robertkowalski committed Apr 13, 2022
1 parent 9e57430 commit 09c40e9
Show file tree
Hide file tree
Showing 4 changed files with 2,298 additions and 2,933 deletions.
15 changes: 15 additions & 0 deletions __tests__/index.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,21 @@ describe("Ansi", () => {
);
});

test("can linkify fuzzy links", () => {
const el = shallow(
React.createElement(
Ansi,
{ linkify: "fuzzy" },
"this is a fuzzy link: example.com"
)
);
expect(el).not.toBeNull();
expect(el.text()).toBe("this is a fuzzy link: example.com");
expect(el.html()).toBe(
'<code><span>this is a fuzzy link: <a href="http://example.com" target="_blank">example.com</a></span></code>'
);
});

describe("useClasses options", () => {
test("can add the font color class", () => {
const el = shallow(
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"license": "BSD-3-Clause",
"dependencies": {
"anser": "^1.4.1",
"escape-carriage": "^1.3.0"
"escape-carriage": "^1.3.0",
"linkify-it": "^3.0.3"
},
"peerDependencies": {
"react": "^16.3.2 || ^17.0.0",
Expand All @@ -33,6 +34,7 @@
"@semantic-release/npm": "^7.0.8",
"@types/enzyme": "^3.10.5",
"@types/jest": "^25.1.4",
"@types/linkify-it": "^3.0.2",
"@types/react": "^16.9.23",
"conventional-changelog-conventionalcommits": "^4.5.0",
"enzyme": "^3.11.0",
Expand Down
80 changes: 77 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Anser, { AnserJsonEntry } from "anser";
import { escapeCarriageReturn } from "escape-carriage";
import linkifyit from "linkify-it";
import * as React from "react";

/**
Expand Down Expand Up @@ -101,9 +102,8 @@ function createStyle(bundle: AnserJsonEntry): React.CSSProperties {
* @param bundle Anser output.
* @param key
*/

function convertBundleIntoReact(
linkify: boolean,
linkify: boolean | "fuzzy",
useClasses: boolean,
bundle: AnserJsonEntry,
key: number
Expand All @@ -119,6 +119,19 @@ function convertBundleIntoReact(
);
}

if (linkify === "fuzzy") {
return linkWithLinkify(bundle, key, style, className);
}

return linkWithClassicMode(bundle, key, style, className);
}

function linkWithClassicMode(
bundle: AnserJsonEntry,
key: number,
style: React.CSSProperties | null,
className: string | null
) {
const content: React.ReactNode[] = [];
const linkRegex = /(\s|^)(https?:\/\/(?:www\.|(?!www))[^\s.]+\.[^\s]{2,}|www\.[^\s]+\.[^\s]{2,})/g;

Expand Down Expand Up @@ -157,9 +170,70 @@ function convertBundleIntoReact(
return React.createElement("span", { style, key, className }, content);
}

function linkWithLinkify(
bundle: AnserJsonEntry,
key: number,
style: React.CSSProperties | null,
className: string | null
): JSX.Element {
const linker = linkifyit({ fuzzyEmail: false }).tlds(["io"], true);

if (!linker.pretest(bundle.content)) {
return React.createElement(
"span",
{ style, key, className },
bundle.content
);
}

const matches = linker.match(bundle.content);

if (!matches) {
return React.createElement(
"span",
{ style, key, className },
bundle.content
);
}

const content: React.ReactNode[] = [
bundle.content.substring(0, matches[0]?.index),
];

matches.forEach((match, i) => {
content.push(
React.createElement(
"a",
{
href: match.url,
target: "_blank",
key: i,
},
bundle.content.substring(match.index, match.lastIndex)
)
);

if (matches[i + 1]) {
content.push(
bundle.content.substring(matches[i].lastIndex, matches[i + 1]?.index)
);
}
});

if (matches[matches.length - 1].lastIndex !== bundle.content.length) {
content.push(
bundle.content.substring(
matches[matches.length - 1].lastIndex,
bundle.content.length
)
);
}
return React.createElement("span", { style, key, className }, content);
}

declare interface Props {
children?: string;
linkify?: boolean;
linkify?: boolean | "fuzzy";
className?: string;
useClasses?: boolean;
}
Expand Down
Loading

0 comments on commit 09c40e9

Please sign in to comment.