thelounge/shared/linkify.ts

96 lines
1.8 KiB
TypeScript
Raw Normal View History

import LinkifyIt, {Match} from "linkify-it";
import tlds from "tlds";
export type LinkPart = {
start: number;
end: number;
link: string;
};
const linkify = LinkifyIt().tlds(tlds).tlds("onion", true);
// Known schemes to detect in text
const commonSchemes = [
"sftp",
"smb",
"file",
"irc",
"ircs",
"svn",
"git",
"steam",
"mumble",
"ts3server",
"svn+ssh",
"ssh",
"gopher",
"gemini",
];
for (const schema of commonSchemes) {
linkify.add(schema + ":", "http:");
}
linkify.add("web+", {
validate(text: string, pos: number, self: LinkifyIt.LinkifyIt) {
const webSchemaRe = /^[a-z]+:/gi;
if (!webSchemaRe.test(text.slice(pos))) {
return 0;
}
const linkEnd = self.testSchemaAt(text, "http:", pos + webSchemaRe.lastIndex);
if (linkEnd === 0) {
return 0;
}
return webSchemaRe.lastIndex + linkEnd;
},
normalize(match) {
match.schema = match.text.slice(0, match.text.indexOf(":") + 1);
},
});
// we must rewrite protocol less urls to http, else if TL is hosted
// on https, this would incorrectly use https for the remote link.
// See https://github.com/thelounge/thelounge/issues/2525
//
// We take the validation logic from linkify and just add our own
// normalizer.
linkify.add("//", {
validate: (linkify as any).__schemas__["//"].validate,
normalize(match) {
match.schema = ""; // this counts as not having a schema
match.url = "http:" + match.url;
},
});
export function findLinks(text: string) {
const matches = linkify.match(text);
if (!matches) {
return [];
}
return matches.map(makeLinkPart);
}
export function findLinksWithSchema(text: string) {
const matches = linkify.match(text);
if (!matches) {
return [];
}
return matches.filter((url) => !!url.schema).map(makeLinkPart);
}
function makeLinkPart(url: Match): LinkPart {
return {
start: url.index,
end: url.lastIndex,
link: url.url,
};
}