🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

linkify-it

Package Overview
Dependencies
Maintainers
1
Versions
32
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

linkify-it - npm Package Compare versions

Comparing version
5.0.2
to
6.0.0
+1
build/index.cjs.js.map
{"version":3,"file":"index.cjs.js","names":[],"sources":["../src/rebuilder.ts","../src/linkifyit.ts"],"sourcesContent":["/* eslint-disable no-return-assign, prefer-regex-literals */\n\nimport { Any, Cc, Z, P } from 'uc.micro'\n\n/**\n * @category types\n */\nexport interface REBuilderOptions {\n '---'?: boolean\n fuzzyIP?: boolean\n schema_names?: string[]\n tlds?: string[]\n urlAuth?: boolean\n maxLength?: number\n}\n\nexport class REBuilder {\n src_Any = Any.source\n src_Cc = Cc.source\n src_Z = Z.source\n src_P = P.source\n // \\p{\\Z\\P\\Cc\\CF} (white spaces + control + format + punctuation)\n src_ZPCc = [this.src_Z, this.src_P, this.src_Cc].join('|')\n // \\p{\\Z\\Cc} (white spaces + control)\n src_ZCc = [this.src_Z, this.src_Cc].join('|')\n cache: Record<string, RegExp | undefined> = {}\n opts: REBuilderOptions = { maxLength: 10000, urlAuth: false, schema_names: [] }\n\n constructor (opts: REBuilderOptions = {}) {\n this.opts = { ...this.opts, ...opts }\n }\n\n set (opts: REBuilderOptions = {}) {\n this.opts = { ...this.opts, ...opts }\n\n this.cache = {}\n return this\n }\n\n escapeRE (str: string) { return str.replace(/[.?*+^$[\\]\\\\(){}|-]/g, '\\\\$&') }\n\n nestedPairRE (open: string, close: string, depth = 4) {\n const openRE = this.escapeRE(open)\n const closeRE = this.escapeRE(close)\n const atom = `(?:(?!${this.src_ZCc}|${openRE}|${closeRE}).)`\n\n let pair = `${openRE}${atom}{0,1000}${closeRE}`\n\n for (let level = 2; level <= depth; level++) {\n pair = `${openRE}(?:${atom}|${pair}){0,1000}${closeRE}`\n }\n\n return pair\n }\n\n // Partials\n\n get_text_separators () {\n // Experimental. List of chars, completely prohibited in links\n // because can separate it from other part of text\n return this.cache.text_separators ??= /[><\\uff5c]/\n }\n\n get_pseudo_letter () {\n return this.cache.src_pseudo_letter ??= new RegExp(\n // All possible word characters (everything without punctuation, spaces & controls)\n // Defined via punctuation & spaces to save space\n // Should be something like \\p{\\L\\N\\S\\M} (\\w but without `_`)\n `(?:(?!${this.get_text_separators().source}|${this.src_ZPCc})${this.src_Any})`\n )\n }\n\n get_ipv4_addr () {\n return this.cache.src_ip4 ??= new RegExp(\n '(?:' +\n '(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])[.]' +\n '){3}' +\n '(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])'\n )\n }\n\n get_ipv6_addr () {\n const h16 = '[0-9A-Fa-f]{1,4}'\n const ls32 = `(?:(?:${h16}:${h16})|${this.get_ipv4_addr().source})`\n\n return this.cache.src_ip6_addr ??= new RegExp(\n '(?:' +\n `(?:${h16}:){6}${ls32}|` +\n `::(?:${h16}:){5}${ls32}|` +\n `(?:${h16})?::(?:${h16}:){4}${ls32}|` +\n `(?:(?:${h16}:){0,1}${h16})?::(?:${h16}:){3}${ls32}|` +\n `(?:(?:${h16}:){0,2}${h16})?::(?:${h16}:){2}${ls32}|` +\n `(?:(?:${h16}:){0,3}${h16})?::${h16}:${ls32}|` +\n `(?:(?:${h16}:){0,4}${h16})?::${ls32}|` +\n `(?:(?:${h16}:){0,5}${h16})?::${h16}|` +\n `(?:(?:${h16}:){0,6}${h16})?::` +\n ')'\n )\n }\n\n get_ipv6_url_host () {\n return this.cache.src_ip6_host ??= new RegExp(\n `\\\\[${this.get_ipv6_addr().source}\\\\]`\n )\n }\n\n get_ipv6_mail_host () {\n return this.cache.src_ipv6_mail_host ??= new RegExp(\n `\\\\[IPv6:${this.get_ipv6_addr().source}\\\\]`\n )\n }\n\n get_auth () {\n return this.cache.src_auth ??= new RegExp(\n // Prohibit any of \"@/[]()\" in user/pass to avoid wrong domain fetch.\n // Length is capped to exclude possible rescans till the end and avoid O(n^2)\n // DoS. No standard limit, just take something reasonable.\n `(?:(?:(?!${this.src_ZCc}|[@/\\\\[\\\\]()]).){1,50}@)?`\n )\n }\n\n get_port () {\n return this.cache.src_port ??= new RegExp(\n '(?::(?:6(?:[0-4]\\\\d{3}|5(?:[0-4]\\\\d{2}|5(?:[0-2]\\\\d|3[0-5])))|[1-5]?\\\\d{1,4}))?'\n )\n }\n\n get_host_terminator () {\n // Force greedy fetch, we should not stop earlier than host part is fully fetched.\n return this.cache.src_host_terminator ??= new RegExp(\n `(?=$|${this.get_text_separators().source}|${this.src_ZPCc})` +\n `(?!${this.opts['---'] ? '-(?!--)|' : '-|'}_|:\\\\d|\\\\.-|\\\\.(?!$|${this.src_ZPCc}))`\n )\n }\n\n get_path_terminator () {\n return this.cache.src_path_terminator ??= new RegExp(\n `${this.src_ZPCc}|${this.get_text_separators().source}`\n // `${this.src_ZCc}|${this.get_text_separators().source}|[()[\\\\]{}.,\"'?!\\\\-;]`\n )\n }\n\n get_path () {\n return this.cache.src_path ??= new RegExp(\n '(?:' +\n '[/?#]' +\n '(?:' +\n `${this.nestedPairRE('[', ']')}|` +\n `${this.nestedPairRE('(', ')')}|` +\n `${this.nestedPairRE('{', '}')}|` +\n `\\\\\"(?:(?!${this.src_ZCc}|[\"]).){1,100}\\\\\"|` +\n `\\\\'(?:(?!${this.src_ZCc}|[']).){1,100}\\\\'|` +\n\n // allow `I'm_king` if no pair found\n `\\\\'(?=${this.get_pseudo_letter().source}|[-])|` +\n\n // 1. google has many dots in \"google search\" links (#66, #81).\n // github has ... in commit range links,\n // Restrict to\n // - english\n // - percent-encoded\n // - parts of file path\n // - params separator\n // until more examples found.\n //\n // 2. Allow `..:XX` for Odysee links (optional `:`), #100\n // This may be narrowed down later via separate rule.\n '\\\\.{2,20}[:]?[a-zA-Z0-9%/&]|' +\n\n `\\\\.(?!${this.src_ZCc}|[.]|$)|` +\n (this.opts['---']\n ? '\\\\-(?!--(?:[^-]|$))(?:-{0,19})|' // `---` => long dash, terminate\n : '\\\\-{1,20}|'\n ) +\n // allow `,,,` in paths\n `,(?!${this.src_ZCc}|$)|` +\n\n // allow `;` if not followed by space-like char\n `;(?!${this.src_ZCc}|$)|` +\n\n // allow `!!!` in paths, but not at the end\n `\\\\!{1,20}(?!${this.src_ZCc}|[!]|$)|` +\n\n `\\\\?(?!${this.src_ZCc}|[?]|$)|` +\n\n this.get_path_extra().source +\n\n // allowed punctuation chars in path.\n '[\\\\\\\\/:%@#&=_~*]|' +\n\n // if no special rules matched, consume all chars except terminators.\n `(?!${this.get_path_terminator().source}).` +\n `){1,${this.opts.maxLength}}` +\n '|\\\\/' +\n ')?'\n )\n }\n\n get_mail_name () {\n return this.cache.src_mail_name ??= new RegExp(\n // RFC 5321 dot-string only (no quoted-string), max 64 ASCII characters.\n \"[-!#$%&'*+/=?^_`{|}~a-zA-Z0-9](?:[-!#$%&'*+/=?^_`{|}~a-zA-Z0-9]|[.](?=[-!#$%&'*+/=?^_`{|}~a-zA-Z0-9])){0,63}\"\n )\n }\n\n get_xn () {\n return this.cache.src_xn ??= new RegExp(\n 'xn--[a-z0-9\\\\-]{1,59}'\n )\n }\n\n get_tld () {\n if (this.cache.tld) return this.cache.tld\n\n const tlds_src = [...new Set(this.opts.tlds || [])].sort().reverse()\n .join('|')\n\n this.cache.tld = new RegExp(\n `${tlds_src || '$#none#$'}|${this.get_xn().source}`\n )\n return this.cache.tld\n }\n\n get_domain_root () {\n return this.cache.src_domain_root ??= new RegExp(\n // More to read about domain names\n // http://serverfault.com/questions/638260/\n\n // Allow letters & digits (http://test1)\n '(?:' +\n this.get_xn().source +\n '|' +\n `${this.get_pseudo_letter().source}{1,63}` +\n ')'\n )\n }\n\n get_domain () {\n return this.cache.src_domain ??= new RegExp(\n '(?:' +\n this.get_xn().source +\n '|' +\n `(?:${this.get_pseudo_letter().source})` +\n '|' +\n `(?:${this.get_pseudo_letter().source}(?:-|${this.get_pseudo_letter().source}){0,61}${this.get_pseudo_letter().source})` +\n ')'\n )\n }\n\n // Host rules, depending on the type\n\n get_url_host_port () {\n return this.cache.url_host_port ??= new RegExp(\n '(?:' +\n // Don't need IP v4 check, because digits are already allowed\n // in normal domain names\n this.get_ipv6_url_host().source +\n '|' +\n `(?:(?:(?:${this.get_domain().source})\\\\.){0,10}${this.get_domain().source})`/* _root */ +\n ')' +\n this.get_port().source +\n this.get_host_terminator().source\n )\n }\n\n get_fuzzy_url_host_port () {\n // - TLD as anchor\n // - No local domains\n return this.cache.fuzzy_url_host_port ??= new RegExp(\n '(?:' +\n (this.opts.fuzzyIP ? this.get_ipv4_addr().source + '|' : '') +\n `(?:(?:(?:${this.get_domain().source})\\\\.){1,10}(?:${this.get_tld().source}))` +\n ')' +\n // No port in fuzzy links to reduce search\n this.get_host_terminator().source\n )\n }\n\n get_mail_host () {\n // Similar to normal url host, but\n // - with different ipv6 format (and without port).\n // - with reduced max subdomains\n return this.cache.src_mail_host ??= new RegExp(\n '(?:' +\n this.get_ipv6_mail_host().source +\n '|' +\n `(?:(?:(?:${this.get_domain().source})\\\\.){0,4}${this.get_domain().source})` +\n ')' +\n this.get_host_terminator().source\n )\n }\n\n get_fuzzy_mail_host () {\n // Similar to normal mail host, but without local domains.\n return this.cache.src_fuzzy_mail_host ??= new RegExp(\n '(?:' +\n this.get_ipv6_mail_host().source +\n '|' +\n `(?:(?:(?:${this.get_domain().source})[.]){1,4}${this.get_domain_root().source})` +\n ')' +\n this.get_host_terminator().source\n )\n }\n\n // Hooks\n\n get_path_extra () {\n return this.cache.src_path_extra ??= new RegExp('')\n }\n\n // \"Public\" rules\n\n get_fuzzy_mail_host_search () {\n return this.cache.mail_fuzzy_host_search ??= new RegExp(\n `@${this.get_fuzzy_mail_host().source}`,\n 'ig'\n )\n }\n\n get_fuzzy_link_search () {\n return this.cache.link_fuzzy_search ??= new RegExp(\n // Fuzzy link can't be prepended with .:/\\- and non punctuation.\n // but can start with > (markdown blockquote)\n `(^|(?![.:/\\\\-_@])(?:[$+<=>^\\`|\\uff5c]|${this.src_ZPCc}))` +\n `(?:(?![$+<=>^\\`|\\uff5c])${this.get_fuzzy_url_host_port().source}${this.get_path().source})`,\n 'ig'\n )\n }\n\n get_http_validator () {\n return this.cache.http_validator ??= new RegExp(\n '\\\\/\\\\/' +\n (this.opts.urlAuth ? this.get_auth().source : '') +\n this.get_url_host_port().source +\n this.get_path().source,\n 'iy'\n )\n }\n\n get_relative_proto_validator () {\n return this.cache.relative_proto_validator ??= new RegExp(\n (this.opts.urlAuth ? this.get_auth().source : '') +\n // Don't allow single-level domains, because of false positives like '//test'\n // with code comments.\n `(?:localhost|${this.get_ipv6_url_host().source}|(?:(?:${this.get_domain().source})[.]){1,10}${this.get_domain_root().source})` +\n this.get_port().source +\n this.get_host_terminator().source +\n this.get_path().source,\n\n 'iy'\n )\n }\n\n get_mail_name_validator () {\n return this.cache.mail_name_validator ??= new RegExp(\n `(?:^|${this.get_text_separators().source}|\"|\\\\(|${this.src_ZCc})` +\n `(${this.get_mail_name().source})$`\n )\n }\n\n get_mailto_validator () {\n return this.cache.mailto_validator ??= new RegExp(\n `${this.get_mail_name().source}@${this.get_mail_host().source}`,\n 'iy'\n )\n }\n\n get_schema_names () {\n return this.cache.schema_names ??= new RegExp((this.opts.schema_names || []).map(name => this.escapeRE(name)).join('|'))\n }\n\n get_schema_search () {\n return this.cache.schema_search ??= new RegExp(\n `(^|(?!_)(?:[><\\uff5c]|${this.src_ZPCc}))(${this.get_schema_names().source})`,\n 'ig'\n )\n }\n\n get_schema_at_start () {\n return this.cache.schema_at_start ??= new RegExp(\n `^${this.get_schema_search().source}`,\n 'i'\n )\n }\n}\n","import { REBuilder } from './rebuilder.ts'\n\n//\n\n/**\n * Recognition options for schemaless links.\n *\n * @category types\n */\nexport interface LinkifyOptions {\n /** Recognize URLs without `http(s)://` prefix. Default `false`. */\n fuzzyLink?: boolean\n /** Recognize emails without `mailto:` prefix. Default `true`. */\n fuzzyEmail?: boolean\n /**\n * Allow IPs in fuzzy links. Can conflict with some texts, like version\n * numbers. Default `false`.\n */\n fuzzyIP?: boolean\n /**\n * Terminate link with `---` if it is considered a long dash. Default `false`.\n */\n '---'?: boolean\n /** Allowed TLDs list for fuzzy links. Replaces the default list when set. */\n tlds?: string[]\n /** Recognize authentication data in URLs. Default `false`. */\n urlAuth?: boolean\n /** Maximum link length. Default `10000`. */\n maxLength?: number\n}\n\nexport interface LinkifyConstructorOptions extends LinkifyOptions {\n /** Custom regular expression builder. */\n rebuilder?: REBuilder\n}\n\n/**\n * Custom schema definition.\n *\n * @category types\n */\nexport interface SchemaOpts {\n /**\n * Checks text after the schema prefix. Should return matched tail length on\n * success, or `0` on fail.\n */\n validate: (text: string, pos: number, self: LinkifyIt) => number\n /**\n * Optional function to normalize `text` and `url` of matched result, for\n * example for `@twitter` mentions.\n */\n normalize?: (match: Match, self: LinkifyIt) => void\n}\n\ntype Schema = Required<SchemaOpts>\n\ninterface MatchCandidate {\n schema: string\n index: number\n lastIndex: number\n}\n\nconst web_schema: Schema = {\n validate: (text: string, pos: number, self: LinkifyIt) => {\n const re = self.re.get_http_validator()\n re.lastIndex = pos\n\n const m = re.exec(text)\n return m ? m[0].length : 0\n },\n normalize: (match: Match, self: LinkifyIt) => self.normalize(match)\n}\n\nconst defaultSchemas: Record<string, Schema> = {\n 'http:': web_schema,\n 'https:': web_schema,\n 'ftp:': web_schema,\n '//': {\n validate: function (text: string, pos: number, self: LinkifyIt) {\n const re = self.re.get_relative_proto_validator()\n re.lastIndex = pos\n\n const m = re.exec(text)\n if (m) {\n // should not be `://` & `///`, that protects from errors in protocol name\n if (pos >= 3 && text[pos - 3] === ':') { return 0 }\n if (pos >= 3 && text[pos - 3] === '/') { return 0 }\n return m[0].length\n }\n return 0\n },\n normalize: (match: Match, self: LinkifyIt) => self.normalize(match)\n },\n 'mailto:': {\n validate: function (text: string, pos: number, self: LinkifyIt) {\n const re = self.re.get_mailto_validator()\n re.lastIndex = pos\n\n const m = re.exec(text)\n return m ? m[0].length : 0\n },\n normalize: (match: Match, self: LinkifyIt) => self.normalize(match)\n }\n}\n\n// List of 2-character tlds (autogenerated by ./support/tlds_2char_gen.js)\nconst tlds_2ch = 'a:cdefgilmnoqrstuwxz|b:abdefghijmnorstvwyz|c:acdfghiklmnoruvwxyz|d:ejkmoz|e:cegrstu|f:ijkmor|g:abdefghilmnpqrstuwy|h:kmnrtu|i:delmnoqrst|j:emop|k:eghimnprwyz|l:abcikrstuvy|m:acdeghklmnopqrstuvwxyz|n:acefgilopruz|o:m|p:aefghklmnrstwy|q:a|r:eosuw|s:abcdeghijklmnortuvxyz|t:cdfghjklmnortvwz|u:agksyz|v:aceginu|w:fs|y:et|z:amw'\n\n// DON'T try to make PRs with changes. Extend TLDs with LinkifyIt.tlds() instead\nconst tlds_default = 'biz|com|edu|gov|net|org|pro|web|xxx|aero|asia|coop|info|museum|name|shop|рф'\n\nfunction unpackTlds (): string[] {\n const result = tlds_default.split('|')\n\n tlds_2ch.split('|').forEach((item) => {\n const sep = item.indexOf(':')\n\n const prefix = item.slice(0, sep)\n\n for (const suffix of item.slice(sep + 1)) {\n result.push(prefix + suffix)\n }\n })\n\n return result\n}\n\nconst defaultOptions: Required<LinkifyOptions> = {\n fuzzyLink: false,\n fuzzyEmail: true,\n fuzzyIP: false,\n '---': false,\n tlds: unpackTlds(),\n urlAuth: false,\n maxLength: 10000\n}\n\n/**\n * Match result returned by {@link LinkifyIt.match} and\n * {@link LinkifyIt.matchAtStart}.\n *\n * @category types\n */\nexport class Match {\n /** Prefix (protocol) for matched string. Empty for fuzzy links. */\n schema: string\n /** First position of matched string. */\n index: number\n /** Next position after matched string. */\n lastIndex: number\n /** Matched string. */\n raw: string\n /** Normalized text of matched string. */\n text: string\n /** Normalized URL of matched string. */\n url: string\n\n constructor (text: string, schema: string, index: number, lastIndex: number) {\n const raw = text.slice(index, lastIndex)\n\n this.schema = schema.toLowerCase()\n this.index = index\n this.lastIndex = lastIndex\n this.raw = raw\n this.text = raw\n this.url = raw\n }\n}\n\n/** Linkifier instance. */\nexport class LinkifyIt {\n __opts__: Required<LinkifyOptions>\n private __schemas__: Record<string, Schema>\n re: REBuilder\n\n /**\n * Creates new linkifier instance.\n *\n * By default understands:\n *\n * - `http(s)://...` , `ftp://...`, `mailto:...` & `//...` links\n * - \"fuzzy\" emails (foo@bar.com).\n *\n * See {@link LinkifyConstructorOptions} for available options.\n *\n * @param options Recognition options.\n *\n * @example\n * ```javascript\n * import { LinkifyIt } from 'linkify-it'\n *\n * const linkify = new LinkifyIt({ fuzzyLink: true })\n *\n * linkify\n * .tlds(require('tlds')) // Reload with full TLD list\n * .tlds('onion', true) // Add unofficial `.onion` domain\n * .add('ftp:', null) // Disable `ftp:` protocol\n * .set({ fuzzyIP: true }) // Enable IPs in fuzzy links\n *\n * console.log(linkify.test('Site github.com!')) // true\n * console.log(linkify.match('Site github.com!'))\n * ```\n */\n constructor (options: LinkifyConstructorOptions = {}) {\n const { rebuilder, ...linkifyOptions } = options\n\n this.__opts__ = { ...defaultOptions, ...linkifyOptions }\n\n this.__schemas__ = { ...defaultSchemas }\n\n this.re = rebuilder || new REBuilder()\n this.re.set({\n ...this.__opts__,\n schema_names: Object.keys(this.__schemas__)\n })\n }\n\n /**\n * Add new rule definition.\n *\n * `schema` is a link prefix (usually, protocol name with `:` at the end,\n * `skype:` for example). `linkify-it` makes sure that prefix is not\n * preceded with alphanumeric char and symbols. Only whitespaces and\n * punctuation allowed.\n *\n * `definition` is a rule to check tail after link prefix. To disable an\n * existing rule, pass `null`.\n *\n * @param schema Rule name (fixed pattern prefix).\n * @param definition Schema definition, or `null` to disable the rule.\n *\n * See [twitter mentions example](https://github.com/markdown-it/linkify-it/blob/master/examples/twitter.mjs).\n */\n add (schema: string, definition: SchemaOpts | null = null): this {\n if (!definition) {\n delete this.__schemas__[schema]\n } else {\n const def = {\n normalize: (match: Match, self: LinkifyIt) => self.normalize(match),\n ...definition\n }\n this.__schemas__[schema] = def\n }\n\n this.re.set({\n ...this.__opts__,\n schema_names: Object.keys(this.__schemas__)\n })\n return this\n }\n\n /**\n * Set recognition options for links without schema.\n *\n * @param options Recognition options.\n */\n set (options: LinkifyOptions = {}): this {\n this.__opts__ = { ...this.__opts__, ...options }\n this.re.set({\n ...this.__opts__,\n schema_names: Object.keys(this.__schemas__)\n })\n return this\n }\n\n /**\n * Searches linkifiable pattern and returns `true` on success or `false` on fail.\n *\n * @param text Text to scan.\n */\n test (text: string): boolean {\n if (!text.length) { return false }\n\n let m, re\n\n // try to scan for link with schema - that's the most simple rule\n re = this.re.get_schema_search()\n re.lastIndex = 0\n while ((m = re.exec(text)) !== null) {\n if (this.testSchemaAt(text, m[2], re.lastIndex)) { return true }\n }\n\n if (this.__opts__.fuzzyLink && this.__schemas__['http:']) {\n // guess schemaless links\n re = this.re.get_fuzzy_link_search()\n re.lastIndex = 0\n if (re.exec(text) !== null) { return true }\n }\n\n if (this.__opts__.fuzzyEmail && this.__schemas__['mailto:']) {\n // guess schemaless emails\n if (text.indexOf('@') >= 0) {\n // We can't skip this check, because this cases are possible:\n // 192.168.1.1@gmail.com, my.in@example.com\n const mailHostRe = this.re.get_fuzzy_mail_host_search()\n const mailNameRe = this.re.get_mail_name_validator()\n mailHostRe.lastIndex = 0\n\n while ((m = mailHostRe.exec(text)) !== null) {\n const name = text.slice(Math.max(0, m.index - 65), m.index)\n if (mailNameRe.test(name)) { return true }\n }\n }\n }\n\n return false\n }\n\n /**\n * Similar to {@link LinkifyIt.test} but checks only specific protocol tail exactly\n * at given position. Returns length of found pattern (0 on fail).\n *\n * @param text Text to scan.\n * @param schema Rule (schema) name.\n * @param pos Text offset to check from.\n */\n testSchemaAt (text: string, schema: string, pos: number): number {\n // If not supported schema check requested - terminate\n if (!this.__schemas__[schema.toLowerCase()]) { return 0 }\n\n return this.__schemas__[schema.toLowerCase()].validate(\n text.slice(0, pos + this.__opts__.maxLength),\n pos,\n this\n )\n }\n\n /**\n * Returns array of found link descriptions or `null` on fail. We strongly\n * recommend to use {@link LinkifyIt.test} first, for best speed.\n *\n * @param text Text to scan.\n */\n match (text: string): Match[] | null {\n const result: Match[] = []\n const schemaRe = this.re.get_schema_search()\n let fuzzyLinkRe: RegExp | undefined\n let mailHostRe: RegExp | undefined\n let mailNameRe: RegExp | undefined\n let fuzzyLinkCandidate: MatchCandidate | undefined\n let fuzzyEmailCandidate: MatchCandidate | undefined\n let schemaPrefix: MatchCandidate | undefined\n let schemaDone = false\n let fuzzyLinkDone = false\n let fuzzyEmailDone = false\n let pos = 0\n\n if (!text.length) { return null }\n\n schemaRe.lastIndex = 0\n\n if (this.__opts__.fuzzyLink && this.__schemas__['http:']) {\n fuzzyLinkRe = this.re.get_fuzzy_link_search()\n fuzzyLinkRe.lastIndex = 0\n }\n\n if (this.__opts__.fuzzyEmail && this.__schemas__['mailto:']) {\n mailHostRe = this.re.get_fuzzy_mail_host_search()\n mailHostRe.lastIndex = 0\n mailNameRe = this.re.get_mail_name_validator()\n }\n\n for (;;) {\n const scanFrom = Math.max(pos - 1, 0)\n\n if (mailHostRe && mailNameRe && !fuzzyEmailDone && (!fuzzyEmailCandidate || fuzzyEmailCandidate.index < pos)) {\n if (mailHostRe.lastIndex < scanFrom) { mailHostRe.lastIndex = scanFrom }\n\n for (;;) {\n const m = mailHostRe.exec(text)\n if (!m) {\n fuzzyEmailDone = true\n fuzzyEmailCandidate = undefined\n break\n }\n\n const name = mailNameRe.exec(text.slice(Math.max(0, m.index - 65), m.index))\n if (!name) { continue }\n\n fuzzyEmailCandidate = {\n schema: 'mailto:',\n index: m.index - name[1].length,\n lastIndex: m.index + m[0].length\n }\n\n if (fuzzyEmailCandidate.index >= pos) { break }\n if (mailHostRe.lastIndex < scanFrom) { mailHostRe.lastIndex = scanFrom }\n }\n }\n\n if (fuzzyLinkRe && !fuzzyLinkDone && (!fuzzyLinkCandidate || fuzzyLinkCandidate.index < pos)) {\n if (fuzzyLinkRe.lastIndex < scanFrom) { fuzzyLinkRe.lastIndex = scanFrom }\n\n for (;;) {\n const m = fuzzyLinkRe.exec(text)\n if (!m) {\n fuzzyLinkDone = true\n fuzzyLinkCandidate = undefined\n break\n }\n\n fuzzyLinkCandidate = {\n schema: '',\n index: m.index + m[1].length,\n lastIndex: m.index + m[0].length\n }\n\n if (fuzzyLinkCandidate.index >= pos) { break }\n if (fuzzyLinkRe.lastIndex < scanFrom) { fuzzyLinkRe.lastIndex = scanFrom }\n }\n }\n\n let fuzzyCandidate = fuzzyEmailCandidate\n if (!fuzzyCandidate ||\n (fuzzyLinkCandidate &&\n (fuzzyLinkCandidate.index < fuzzyCandidate.index ||\n (fuzzyLinkCandidate.index === fuzzyCandidate.index && fuzzyLinkCandidate.lastIndex > fuzzyCandidate.lastIndex)))) {\n fuzzyCandidate = fuzzyLinkCandidate\n }\n\n let schemaCandidate: MatchCandidate | undefined\n\n if (!schemaDone) {\n for (;;) {\n if (!schemaPrefix) {\n if (schemaRe.lastIndex < scanFrom) { schemaRe.lastIndex = scanFrom }\n\n const m = schemaRe.exec(text)\n if (!m) {\n schemaDone = true\n break\n }\n\n schemaPrefix = {\n schema: m[2],\n index: m.index + m[1].length,\n lastIndex: m.index + m[0].length\n }\n }\n\n if (schemaPrefix.index < pos) {\n schemaPrefix = undefined\n continue\n }\n\n if (fuzzyCandidate && schemaPrefix.index > fuzzyCandidate.index) { break }\n\n const prefix = schemaPrefix\n schemaPrefix = undefined\n\n const len = this.testSchemaAt(text, prefix.schema, prefix.lastIndex)\n if (len) {\n schemaCandidate = {\n schema: prefix.schema,\n index: prefix.index,\n lastIndex: prefix.lastIndex + len\n }\n break\n }\n }\n }\n\n let candidate = schemaCandidate\n if (!candidate ||\n (fuzzyEmailCandidate &&\n (fuzzyEmailCandidate.index < candidate.index ||\n (fuzzyEmailCandidate.index === candidate.index && fuzzyEmailCandidate.lastIndex > candidate.lastIndex)))) {\n candidate = fuzzyEmailCandidate\n }\n if (!candidate ||\n (fuzzyLinkCandidate &&\n (fuzzyLinkCandidate.index < candidate.index ||\n (fuzzyLinkCandidate.index === candidate.index && fuzzyLinkCandidate.lastIndex > candidate.lastIndex)))) {\n candidate = fuzzyLinkCandidate\n }\n\n if (!candidate) { break }\n\n if (candidate === fuzzyEmailCandidate) {\n fuzzyEmailCandidate = undefined\n } else if (candidate === fuzzyLinkCandidate) {\n fuzzyLinkCandidate = undefined\n }\n\n const match = new Match(text, candidate.schema, candidate.index, candidate.lastIndex)\n if (match.schema) {\n this.__schemas__[match.schema].normalize(match, this)\n } else {\n this.normalize(match)\n }\n result.push(match)\n pos = candidate.lastIndex\n }\n\n if (result.length) {\n return result\n }\n\n return null\n }\n\n /**\n * Returns fully-formed (not fuzzy) link if it starts at the beginning\n * of the string, and null otherwise.\n *\n * @param text Text to scan.\n */\n matchAtStart (text: string): Match | null {\n if (!text.length) return null\n\n const m = this.re.get_schema_at_start().exec(text)\n if (!m) return null\n\n const len = this.testSchemaAt(text, m[2], m[0].length)\n if (!len) return null\n\n const match = new Match(text, m[2], m.index + m[1].length, m.index + m[0].length + len)\n\n this.__schemas__[match.schema].normalize(match, this)\n return match\n }\n\n /**\n * Load (or merge) new TLDs list. Those are used for fuzzy links (without\n * prefix) to avoid false positives. By default this algorithm is used:\n *\n * - hostname with any 2-letter root zones are ok.\n * - biz|com|edu|gov|net|org|pro|web|xxx|aero|asia|coop|info|museum|name|shop|рф\n * are ok.\n * - encoded (`xn--...`) root zones are ok.\n *\n * If list is replaced, then exact match for 2-chars root zones will be checked.\n *\n * @param list List of TLDs.\n * @param keepOld Merge with current list if `true` (`false` by default).\n */\n tlds (list: string | string[], keepOld = false): this {\n list = Array.isArray(list) ? list : [list]\n\n if (!keepOld) {\n this.__opts__.tlds = list\n } else {\n this.__opts__.tlds = this.__opts__.tlds.concat(list)\n }\n\n this.re.set({\n ...this.__opts__,\n schema_names: Object.keys(this.__schemas__)\n })\n return this\n }\n\n /**\n * Default normalizer (if schema does not define its own).\n *\n * @param match Match to normalize.\n */\n normalize (match: Match): void {\n // Do minimal possible changes by default. Need to collect feedback prior\n // to move forward https://github.com/markdown-it/linkify-it/issues/1\n\n if (!match.schema) { match.url = `http://${match.url}` }\n\n if (match.schema === 'mailto:' && !/^mailto:/i.test(match.url)) {\n match.url = `mailto:${match.url}`\n }\n }\n}\n\nfunction linkifyit (options: LinkifyConstructorOptions = {}): LinkifyIt {\n return new LinkifyIt(options)\n}\n\nexport { linkifyit }\n"],"mappings":"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"}
/**
* @category types
*/
interface REBuilderOptions {
'---'?: boolean;
fuzzyIP?: boolean;
schema_names?: string[];
tlds?: string[];
urlAuth?: boolean;
maxLength?: number;
}
declare class REBuilder {
src_Any: string;
src_Cc: string;
src_Z: string;
src_P: string;
src_ZPCc: string;
src_ZCc: string;
cache: Record<string, RegExp | undefined>;
opts: REBuilderOptions;
constructor(opts?: REBuilderOptions);
set(opts?: REBuilderOptions): this;
escapeRE(str: string): string;
nestedPairRE(open: string, close: string, depth?: number): string;
get_text_separators(): RegExp;
get_pseudo_letter(): RegExp;
get_ipv4_addr(): RegExp;
get_ipv6_addr(): RegExp;
get_ipv6_url_host(): RegExp;
get_ipv6_mail_host(): RegExp;
get_auth(): RegExp;
get_port(): RegExp;
get_host_terminator(): RegExp;
get_path_terminator(): RegExp;
get_path(): RegExp;
get_mail_name(): RegExp;
get_xn(): RegExp;
get_tld(): RegExp;
get_domain_root(): RegExp;
get_domain(): RegExp;
get_url_host_port(): RegExp;
get_fuzzy_url_host_port(): RegExp;
get_mail_host(): RegExp;
get_fuzzy_mail_host(): RegExp;
get_path_extra(): RegExp;
get_fuzzy_mail_host_search(): RegExp;
get_fuzzy_link_search(): RegExp;
get_http_validator(): RegExp;
get_relative_proto_validator(): RegExp;
get_mail_name_validator(): RegExp;
get_mailto_validator(): RegExp;
get_schema_names(): RegExp;
get_schema_search(): RegExp;
get_schema_at_start(): RegExp;
}
/**
* Recognition options for schemaless links.
*
* @category types
*/
interface LinkifyOptions {
/** Recognize URLs without `http(s)://` prefix. Default `false`. */
fuzzyLink?: boolean;
/** Recognize emails without `mailto:` prefix. Default `true`. */
fuzzyEmail?: boolean;
/**
* Allow IPs in fuzzy links. Can conflict with some texts, like version
* numbers. Default `false`.
*/
fuzzyIP?: boolean;
/**
* Terminate link with `---` if it is considered a long dash. Default `false`.
*/
'---'?: boolean;
/** Allowed TLDs list for fuzzy links. Replaces the default list when set. */
tlds?: string[];
/** Recognize authentication data in URLs. Default `false`. */
urlAuth?: boolean;
/** Maximum link length. Default `10000`. */
maxLength?: number;
}
interface LinkifyConstructorOptions extends LinkifyOptions {
/** Custom regular expression builder. */
rebuilder?: REBuilder;
}
/**
* Custom schema definition.
*
* @category types
*/
interface SchemaOpts {
/**
* Checks text after the schema prefix. Should return matched tail length on
* success, or `0` on fail.
*/
validate: (text: string, pos: number, self: LinkifyIt) => number;
/**
* Optional function to normalize `text` and `url` of matched result, for
* example for `@twitter` mentions.
*/
normalize?: (match: Match, self: LinkifyIt) => void;
}
/**
* Match result returned by {@link LinkifyIt.match} and
* {@link LinkifyIt.matchAtStart}.
*
* @category types
*/
declare class Match {
/** Prefix (protocol) for matched string. Empty for fuzzy links. */
schema: string;
/** First position of matched string. */
index: number;
/** Next position after matched string. */
lastIndex: number;
/** Matched string. */
raw: string;
/** Normalized text of matched string. */
text: string;
/** Normalized URL of matched string. */
url: string;
constructor(text: string, schema: string, index: number, lastIndex: number);
}
/** Linkifier instance. */
declare class LinkifyIt {
__opts__: Required<LinkifyOptions>;
private __schemas__;
re: REBuilder;
/**
* Creates new linkifier instance.
*
* By default understands:
*
* - `http(s)://...` , `ftp://...`, `mailto:...` & `//...` links
* - "fuzzy" emails (foo@bar.com).
*
* See {@link LinkifyConstructorOptions} for available options.
*
* @param options Recognition options.
*
* @example
* ```javascript
* import { LinkifyIt } from 'linkify-it'
*
* const linkify = new LinkifyIt({ fuzzyLink: true })
*
* linkify
* .tlds(require('tlds')) // Reload with full TLD list
* .tlds('onion', true) // Add unofficial `.onion` domain
* .add('ftp:', null) // Disable `ftp:` protocol
* .set({ fuzzyIP: true }) // Enable IPs in fuzzy links
*
* console.log(linkify.test('Site github.com!')) // true
* console.log(linkify.match('Site github.com!'))
* ```
*/
constructor(options?: LinkifyConstructorOptions);
/**
* Add new rule definition.
*
* `schema` is a link prefix (usually, protocol name with `:` at the end,
* `skype:` for example). `linkify-it` makes sure that prefix is not
* preceded with alphanumeric char and symbols. Only whitespaces and
* punctuation allowed.
*
* `definition` is a rule to check tail after link prefix. To disable an
* existing rule, pass `null`.
*
* @param schema Rule name (fixed pattern prefix).
* @param definition Schema definition, or `null` to disable the rule.
*
* See [twitter mentions example](https://github.com/markdown-it/linkify-it/blob/master/examples/twitter.mjs).
*/
add(schema: string, definition?: SchemaOpts | null): this;
/**
* Set recognition options for links without schema.
*
* @param options Recognition options.
*/
set(options?: LinkifyOptions): this;
/**
* Searches linkifiable pattern and returns `true` on success or `false` on fail.
*
* @param text Text to scan.
*/
test(text: string): boolean;
/**
* Similar to {@link LinkifyIt.test} but checks only specific protocol tail exactly
* at given position. Returns length of found pattern (0 on fail).
*
* @param text Text to scan.
* @param schema Rule (schema) name.
* @param pos Text offset to check from.
*/
testSchemaAt(text: string, schema: string, pos: number): number;
/**
* Returns array of found link descriptions or `null` on fail. We strongly
* recommend to use {@link LinkifyIt.test} first, for best speed.
*
* @param text Text to scan.
*/
match(text: string): Match[] | null;
/**
* Returns fully-formed (not fuzzy) link if it starts at the beginning
* of the string, and null otherwise.
*
* @param text Text to scan.
*/
matchAtStart(text: string): Match | null;
/**
* Load (or merge) new TLDs list. Those are used for fuzzy links (without
* prefix) to avoid false positives. By default this algorithm is used:
*
* - hostname with any 2-letter root zones are ok.
* - biz|com|edu|gov|net|org|pro|web|xxx|aero|asia|coop|info|museum|name|shop|рф
* are ok.
* - encoded (`xn--...`) root zones are ok.
*
* If list is replaced, then exact match for 2-chars root zones will be checked.
*
* @param list List of TLDs.
* @param keepOld Merge with current list if `true` (`false` by default).
*/
tlds(list: string | string[], keepOld?: boolean): this;
/**
* Default normalizer (if schema does not define its own).
*
* @param match Match to normalize.
*/
normalize(match: Match): void;
}
declare function linkifyit(options?: LinkifyConstructorOptions): LinkifyIt;
export { LinkifyIt, Match, REBuilder, linkifyit };
export type { LinkifyConstructorOptions, LinkifyOptions, REBuilderOptions, SchemaOpts };
import { Any, Cc, P, Z } from "uc.micro";
//#region src/rebuilder.ts
var REBuilder = class {
src_Any = Any.source;
src_Cc = Cc.source;
src_Z = Z.source;
src_P = P.source;
src_ZPCc = [
this.src_Z,
this.src_P,
this.src_Cc
].join("|");
src_ZCc = [this.src_Z, this.src_Cc].join("|");
cache = {};
opts = {
maxLength: 1e4,
urlAuth: false,
schema_names: []
};
constructor(opts = {}) {
this.opts = {
...this.opts,
...opts
};
}
set(opts = {}) {
this.opts = {
...this.opts,
...opts
};
this.cache = {};
return this;
}
escapeRE(str) {
return str.replace(/[.?*+^$[\]\\(){}|-]/g, "\\$&");
}
nestedPairRE(open, close, depth = 4) {
const openRE = this.escapeRE(open);
const closeRE = this.escapeRE(close);
const atom = `(?:(?!${this.src_ZCc}|${openRE}|${closeRE}).)`;
let pair = `${openRE}${atom}{0,1000}${closeRE}`;
for (let level = 2; level <= depth; level++) pair = `${openRE}(?:${atom}|${pair}){0,1000}${closeRE}`;
return pair;
}
get_text_separators() {
return this.cache.text_separators ??= /[><\uff5c]/;
}
get_pseudo_letter() {
return this.cache.src_pseudo_letter ??= new RegExp(`(?:(?!${this.get_text_separators().source}|${this.src_ZPCc})${this.src_Any})`);
}
get_ipv4_addr() {
return this.cache.src_ip4 ??= /* @__PURE__ */ new RegExp("(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])[.]){3}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])");
}
get_ipv6_addr() {
const h16 = "[0-9A-Fa-f]{1,4}";
const ls32 = `(?:(?:${h16}:${h16})|${this.get_ipv4_addr().source})`;
return this.cache.src_ip6_addr ??= new RegExp(`(?:(?:${h16}:){6}${ls32}|::(?:${h16}:){5}${ls32}|(?:${h16})?::(?:${h16}:){4}${ls32}|(?:(?:${h16}:){0,1}${h16})?::(?:${h16}:){3}${ls32}|(?:(?:${h16}:){0,2}${h16})?::(?:${h16}:){2}${ls32}|(?:(?:${h16}:){0,3}${h16})?::${h16}:${ls32}|(?:(?:${h16}:){0,4}${h16})?::${ls32}|(?:(?:${h16}:){0,5}${h16})?::${h16}|(?:(?:${h16}:){0,6}${h16})?::)`);
}
get_ipv6_url_host() {
return this.cache.src_ip6_host ??= new RegExp(`\\[${this.get_ipv6_addr().source}\\]`);
}
get_ipv6_mail_host() {
return this.cache.src_ipv6_mail_host ??= new RegExp(`\\[IPv6:${this.get_ipv6_addr().source}\\]`);
}
get_auth() {
return this.cache.src_auth ??= new RegExp(`(?:(?:(?!${this.src_ZCc}|[@/\\[\\]()]).){1,50}@)?`);
}
get_port() {
return this.cache.src_port ??= /* @__PURE__ */ new RegExp("(?::(?:6(?:[0-4]\\d{3}|5(?:[0-4]\\d{2}|5(?:[0-2]\\d|3[0-5])))|[1-5]?\\d{1,4}))?");
}
get_host_terminator() {
return this.cache.src_host_terminator ??= new RegExp(`(?=$|${this.get_text_separators().source}|${this.src_ZPCc})(?!${this.opts["---"] ? "-(?!--)|" : "-|"}_|:\\d|\\.-|\\.(?!$|${this.src_ZPCc}))`);
}
get_path_terminator() {
return this.cache.src_path_terminator ??= new RegExp(`${this.src_ZPCc}|${this.get_text_separators().source}`);
}
get_path() {
return this.cache.src_path ??= new RegExp(`(?:[/?#](?:${this.nestedPairRE("[", "]")}|${this.nestedPairRE("(", ")")}|${this.nestedPairRE("{", "}")}|\\"(?:(?!${this.src_ZCc}|["]).){1,100}\\"|\\'(?:(?!${this.src_ZCc}|[']).){1,100}\\'|\\'(?=${this.get_pseudo_letter().source}|[-])|\\.{2,20}[:]?[a-zA-Z0-9%/&]|\\.(?!${this.src_ZCc}|[.]|$)|` + (this.opts["---"] ? "\\-(?!--(?:[^-]|$))(?:-{0,19})|" : "\\-{1,20}|") + `,(?!${this.src_ZCc}|$)|;(?!${this.src_ZCc}|$)|\\!{1,20}(?!${this.src_ZCc}|[!]|$)|\\?(?!${this.src_ZCc}|[?]|$)|` + this.get_path_extra().source + `[\\\\/:%@#&=_~*]|(?!${this.get_path_terminator().source}).){1,${this.opts.maxLength}}|\\/)?`);
}
get_mail_name() {
return this.cache.src_mail_name ??= /* @__PURE__ */ new RegExp("[-!#$%&'*+/=?^_`{|}~a-zA-Z0-9](?:[-!#$%&'*+/=?^_`{|}~a-zA-Z0-9]|[.](?=[-!#$%&'*+/=?^_`{|}~a-zA-Z0-9])){0,63}");
}
get_xn() {
return this.cache.src_xn ??= /* @__PURE__ */ new RegExp("xn--[a-z0-9\\-]{1,59}");
}
get_tld() {
if (this.cache.tld) return this.cache.tld;
const tlds_src = [...new Set(this.opts.tlds || [])].sort().reverse().join("|");
this.cache.tld = new RegExp(`${tlds_src || "$#none#$"}|${this.get_xn().source}`);
return this.cache.tld;
}
get_domain_root() {
return this.cache.src_domain_root ??= new RegExp("(?:" + this.get_xn().source + `|${this.get_pseudo_letter().source}{1,63})`);
}
get_domain() {
return this.cache.src_domain ??= new RegExp("(?:" + this.get_xn().source + `|(?:${this.get_pseudo_letter().source})|(?:${this.get_pseudo_letter().source}(?:-|${this.get_pseudo_letter().source}){0,61}${this.get_pseudo_letter().source}))`);
}
get_url_host_port() {
return this.cache.url_host_port ??= new RegExp("(?:" + this.get_ipv6_url_host().source + `|(?:(?:(?:${this.get_domain().source})\\.){0,10}${this.get_domain().source}))` + this.get_port().source + this.get_host_terminator().source);
}
get_fuzzy_url_host_port() {
return this.cache.fuzzy_url_host_port ??= new RegExp("(?:" + (this.opts.fuzzyIP ? this.get_ipv4_addr().source + "|" : "") + `(?:(?:(?:${this.get_domain().source})\\.){1,10}(?:${this.get_tld().source})))` + this.get_host_terminator().source);
}
get_mail_host() {
return this.cache.src_mail_host ??= new RegExp("(?:" + this.get_ipv6_mail_host().source + `|(?:(?:(?:${this.get_domain().source})\\.){0,4}${this.get_domain().source}))` + this.get_host_terminator().source);
}
get_fuzzy_mail_host() {
return this.cache.src_fuzzy_mail_host ??= new RegExp("(?:" + this.get_ipv6_mail_host().source + `|(?:(?:(?:${this.get_domain().source})[.]){1,4}${this.get_domain_root().source}))` + this.get_host_terminator().source);
}
get_path_extra() {
return this.cache.src_path_extra ??= /* @__PURE__ */ new RegExp("");
}
get_fuzzy_mail_host_search() {
return this.cache.mail_fuzzy_host_search ??= new RegExp(`@${this.get_fuzzy_mail_host().source}`, "ig");
}
get_fuzzy_link_search() {
return this.cache.link_fuzzy_search ??= new RegExp(`(^|(?![.:/\\-_@])(?:[$+<=>^\`|\uff5c]|${this.src_ZPCc}))(?:(?![$+<=>^\`|\uff5c])${this.get_fuzzy_url_host_port().source}${this.get_path().source})`, "ig");
}
get_http_validator() {
return this.cache.http_validator ??= new RegExp("\\/\\/" + (this.opts.urlAuth ? this.get_auth().source : "") + this.get_url_host_port().source + this.get_path().source, "iy");
}
get_relative_proto_validator() {
return this.cache.relative_proto_validator ??= new RegExp((this.opts.urlAuth ? this.get_auth().source : "") + `(?:localhost|${this.get_ipv6_url_host().source}|(?:(?:${this.get_domain().source})[.]){1,10}${this.get_domain_root().source})` + this.get_port().source + this.get_host_terminator().source + this.get_path().source, "iy");
}
get_mail_name_validator() {
return this.cache.mail_name_validator ??= new RegExp(`(?:^|${this.get_text_separators().source}|"|\\(|${this.src_ZCc})(${this.get_mail_name().source})$`);
}
get_mailto_validator() {
return this.cache.mailto_validator ??= new RegExp(`${this.get_mail_name().source}@${this.get_mail_host().source}`, "iy");
}
get_schema_names() {
return this.cache.schema_names ??= new RegExp((this.opts.schema_names || []).map((name) => this.escapeRE(name)).join("|"));
}
get_schema_search() {
return this.cache.schema_search ??= new RegExp(`(^|(?!_)(?:[><\uff5c]|${this.src_ZPCc}))(${this.get_schema_names().source})`, "ig");
}
get_schema_at_start() {
return this.cache.schema_at_start ??= new RegExp(`^${this.get_schema_search().source}`, "i");
}
};
//#endregion
//#region src/linkifyit.ts
var web_schema = {
validate: (text, pos, self) => {
const re = self.re.get_http_validator();
re.lastIndex = pos;
const m = re.exec(text);
return m ? m[0].length : 0;
},
normalize: (match, self) => self.normalize(match)
};
var defaultSchemas = {
"http:": web_schema,
"https:": web_schema,
"ftp:": web_schema,
"//": {
validate: function(text, pos, self) {
const re = self.re.get_relative_proto_validator();
re.lastIndex = pos;
const m = re.exec(text);
if (m) {
if (pos >= 3 && text[pos - 3] === ":") return 0;
if (pos >= 3 && text[pos - 3] === "/") return 0;
return m[0].length;
}
return 0;
},
normalize: (match, self) => self.normalize(match)
},
"mailto:": {
validate: function(text, pos, self) {
const re = self.re.get_mailto_validator();
re.lastIndex = pos;
const m = re.exec(text);
return m ? m[0].length : 0;
},
normalize: (match, self) => self.normalize(match)
}
};
var tlds_2ch = "a:cdefgilmnoqrstuwxz|b:abdefghijmnorstvwyz|c:acdfghiklmnoruvwxyz|d:ejkmoz|e:cegrstu|f:ijkmor|g:abdefghilmnpqrstuwy|h:kmnrtu|i:delmnoqrst|j:emop|k:eghimnprwyz|l:abcikrstuvy|m:acdeghklmnopqrstuvwxyz|n:acefgilopruz|o:m|p:aefghklmnrstwy|q:a|r:eosuw|s:abcdeghijklmnortuvxyz|t:cdfghjklmnortvwz|u:agksyz|v:aceginu|w:fs|y:et|z:amw";
var tlds_default = "biz|com|edu|gov|net|org|pro|web|xxx|aero|asia|coop|info|museum|name|shop|рф";
function unpackTlds() {
const result = tlds_default.split("|");
tlds_2ch.split("|").forEach((item) => {
const sep = item.indexOf(":");
const prefix = item.slice(0, sep);
for (const suffix of item.slice(sep + 1)) result.push(prefix + suffix);
});
return result;
}
var defaultOptions = {
fuzzyLink: false,
fuzzyEmail: true,
fuzzyIP: false,
"---": false,
tlds: unpackTlds(),
urlAuth: false,
maxLength: 1e4
};
/**
* Match result returned by {@link LinkifyIt.match} and
* {@link LinkifyIt.matchAtStart}.
*
* @category types
*/
var Match = class {
/** Prefix (protocol) for matched string. Empty for fuzzy links. */
schema;
/** First position of matched string. */
index;
/** Next position after matched string. */
lastIndex;
/** Matched string. */
raw;
/** Normalized text of matched string. */
text;
/** Normalized URL of matched string. */
url;
constructor(text, schema, index, lastIndex) {
const raw = text.slice(index, lastIndex);
this.schema = schema.toLowerCase();
this.index = index;
this.lastIndex = lastIndex;
this.raw = raw;
this.text = raw;
this.url = raw;
}
};
/** Linkifier instance. */
var LinkifyIt = class {
__opts__;
__schemas__;
re;
/**
* Creates new linkifier instance.
*
* By default understands:
*
* - `http(s)://...` , `ftp://...`, `mailto:...` & `//...` links
* - "fuzzy" emails (foo@bar.com).
*
* See {@link LinkifyConstructorOptions} for available options.
*
* @param options Recognition options.
*
* @example
* ```javascript
* import { LinkifyIt } from 'linkify-it'
*
* const linkify = new LinkifyIt({ fuzzyLink: true })
*
* linkify
* .tlds(require('tlds')) // Reload with full TLD list
* .tlds('onion', true) // Add unofficial `.onion` domain
* .add('ftp:', null) // Disable `ftp:` protocol
* .set({ fuzzyIP: true }) // Enable IPs in fuzzy links
*
* console.log(linkify.test('Site github.com!')) // true
* console.log(linkify.match('Site github.com!'))
* ```
*/
constructor(options = {}) {
const { rebuilder, ...linkifyOptions } = options;
this.__opts__ = {
...defaultOptions,
...linkifyOptions
};
this.__schemas__ = { ...defaultSchemas };
this.re = rebuilder || new REBuilder();
this.re.set({
...this.__opts__,
schema_names: Object.keys(this.__schemas__)
});
}
/**
* Add new rule definition.
*
* `schema` is a link prefix (usually, protocol name with `:` at the end,
* `skype:` for example). `linkify-it` makes sure that prefix is not
* preceded with alphanumeric char and symbols. Only whitespaces and
* punctuation allowed.
*
* `definition` is a rule to check tail after link prefix. To disable an
* existing rule, pass `null`.
*
* @param schema Rule name (fixed pattern prefix).
* @param definition Schema definition, or `null` to disable the rule.
*
* See [twitter mentions example](https://github.com/markdown-it/linkify-it/blob/master/examples/twitter.mjs).
*/
add(schema, definition = null) {
if (!definition) delete this.__schemas__[schema];
else {
const def = {
normalize: (match, self) => self.normalize(match),
...definition
};
this.__schemas__[schema] = def;
}
this.re.set({
...this.__opts__,
schema_names: Object.keys(this.__schemas__)
});
return this;
}
/**
* Set recognition options for links without schema.
*
* @param options Recognition options.
*/
set(options = {}) {
this.__opts__ = {
...this.__opts__,
...options
};
this.re.set({
...this.__opts__,
schema_names: Object.keys(this.__schemas__)
});
return this;
}
/**
* Searches linkifiable pattern and returns `true` on success or `false` on fail.
*
* @param text Text to scan.
*/
test(text) {
if (!text.length) return false;
let m, re;
re = this.re.get_schema_search();
re.lastIndex = 0;
while ((m = re.exec(text)) !== null) if (this.testSchemaAt(text, m[2], re.lastIndex)) return true;
if (this.__opts__.fuzzyLink && this.__schemas__["http:"]) {
re = this.re.get_fuzzy_link_search();
re.lastIndex = 0;
if (re.exec(text) !== null) return true;
}
if (this.__opts__.fuzzyEmail && this.__schemas__["mailto:"]) {
if (text.indexOf("@") >= 0) {
const mailHostRe = this.re.get_fuzzy_mail_host_search();
const mailNameRe = this.re.get_mail_name_validator();
mailHostRe.lastIndex = 0;
while ((m = mailHostRe.exec(text)) !== null) {
const name = text.slice(Math.max(0, m.index - 65), m.index);
if (mailNameRe.test(name)) return true;
}
}
}
return false;
}
/**
* Similar to {@link LinkifyIt.test} but checks only specific protocol tail exactly
* at given position. Returns length of found pattern (0 on fail).
*
* @param text Text to scan.
* @param schema Rule (schema) name.
* @param pos Text offset to check from.
*/
testSchemaAt(text, schema, pos) {
if (!this.__schemas__[schema.toLowerCase()]) return 0;
return this.__schemas__[schema.toLowerCase()].validate(text.slice(0, pos + this.__opts__.maxLength), pos, this);
}
/**
* Returns array of found link descriptions or `null` on fail. We strongly
* recommend to use {@link LinkifyIt.test} first, for best speed.
*
* @param text Text to scan.
*/
match(text) {
const result = [];
const schemaRe = this.re.get_schema_search();
let fuzzyLinkRe;
let mailHostRe;
let mailNameRe;
let fuzzyLinkCandidate;
let fuzzyEmailCandidate;
let schemaPrefix;
let schemaDone = false;
let fuzzyLinkDone = false;
let fuzzyEmailDone = false;
let pos = 0;
if (!text.length) return null;
schemaRe.lastIndex = 0;
if (this.__opts__.fuzzyLink && this.__schemas__["http:"]) {
fuzzyLinkRe = this.re.get_fuzzy_link_search();
fuzzyLinkRe.lastIndex = 0;
}
if (this.__opts__.fuzzyEmail && this.__schemas__["mailto:"]) {
mailHostRe = this.re.get_fuzzy_mail_host_search();
mailHostRe.lastIndex = 0;
mailNameRe = this.re.get_mail_name_validator();
}
for (;;) {
const scanFrom = Math.max(pos - 1, 0);
if (mailHostRe && mailNameRe && !fuzzyEmailDone && (!fuzzyEmailCandidate || fuzzyEmailCandidate.index < pos)) {
if (mailHostRe.lastIndex < scanFrom) mailHostRe.lastIndex = scanFrom;
for (;;) {
const m = mailHostRe.exec(text);
if (!m) {
fuzzyEmailDone = true;
fuzzyEmailCandidate = void 0;
break;
}
const name = mailNameRe.exec(text.slice(Math.max(0, m.index - 65), m.index));
if (!name) continue;
fuzzyEmailCandidate = {
schema: "mailto:",
index: m.index - name[1].length,
lastIndex: m.index + m[0].length
};
if (fuzzyEmailCandidate.index >= pos) break;
if (mailHostRe.lastIndex < scanFrom) mailHostRe.lastIndex = scanFrom;
}
}
if (fuzzyLinkRe && !fuzzyLinkDone && (!fuzzyLinkCandidate || fuzzyLinkCandidate.index < pos)) {
if (fuzzyLinkRe.lastIndex < scanFrom) fuzzyLinkRe.lastIndex = scanFrom;
for (;;) {
const m = fuzzyLinkRe.exec(text);
if (!m) {
fuzzyLinkDone = true;
fuzzyLinkCandidate = void 0;
break;
}
fuzzyLinkCandidate = {
schema: "",
index: m.index + m[1].length,
lastIndex: m.index + m[0].length
};
if (fuzzyLinkCandidate.index >= pos) break;
if (fuzzyLinkRe.lastIndex < scanFrom) fuzzyLinkRe.lastIndex = scanFrom;
}
}
let fuzzyCandidate = fuzzyEmailCandidate;
if (!fuzzyCandidate || fuzzyLinkCandidate && (fuzzyLinkCandidate.index < fuzzyCandidate.index || fuzzyLinkCandidate.index === fuzzyCandidate.index && fuzzyLinkCandidate.lastIndex > fuzzyCandidate.lastIndex)) fuzzyCandidate = fuzzyLinkCandidate;
let schemaCandidate;
if (!schemaDone) for (;;) {
if (!schemaPrefix) {
if (schemaRe.lastIndex < scanFrom) schemaRe.lastIndex = scanFrom;
const m = schemaRe.exec(text);
if (!m) {
schemaDone = true;
break;
}
schemaPrefix = {
schema: m[2],
index: m.index + m[1].length,
lastIndex: m.index + m[0].length
};
}
if (schemaPrefix.index < pos) {
schemaPrefix = void 0;
continue;
}
if (fuzzyCandidate && schemaPrefix.index > fuzzyCandidate.index) break;
const prefix = schemaPrefix;
schemaPrefix = void 0;
const len = this.testSchemaAt(text, prefix.schema, prefix.lastIndex);
if (len) {
schemaCandidate = {
schema: prefix.schema,
index: prefix.index,
lastIndex: prefix.lastIndex + len
};
break;
}
}
let candidate = schemaCandidate;
if (!candidate || fuzzyEmailCandidate && (fuzzyEmailCandidate.index < candidate.index || fuzzyEmailCandidate.index === candidate.index && fuzzyEmailCandidate.lastIndex > candidate.lastIndex)) candidate = fuzzyEmailCandidate;
if (!candidate || fuzzyLinkCandidate && (fuzzyLinkCandidate.index < candidate.index || fuzzyLinkCandidate.index === candidate.index && fuzzyLinkCandidate.lastIndex > candidate.lastIndex)) candidate = fuzzyLinkCandidate;
if (!candidate) break;
if (candidate === fuzzyEmailCandidate) fuzzyEmailCandidate = void 0;
else if (candidate === fuzzyLinkCandidate) fuzzyLinkCandidate = void 0;
const match = new Match(text, candidate.schema, candidate.index, candidate.lastIndex);
if (match.schema) this.__schemas__[match.schema].normalize(match, this);
else this.normalize(match);
result.push(match);
pos = candidate.lastIndex;
}
if (result.length) return result;
return null;
}
/**
* Returns fully-formed (not fuzzy) link if it starts at the beginning
* of the string, and null otherwise.
*
* @param text Text to scan.
*/
matchAtStart(text) {
if (!text.length) return null;
const m = this.re.get_schema_at_start().exec(text);
if (!m) return null;
const len = this.testSchemaAt(text, m[2], m[0].length);
if (!len) return null;
const match = new Match(text, m[2], m.index + m[1].length, m.index + m[0].length + len);
this.__schemas__[match.schema].normalize(match, this);
return match;
}
/**
* Load (or merge) new TLDs list. Those are used for fuzzy links (without
* prefix) to avoid false positives. By default this algorithm is used:
*
* - hostname with any 2-letter root zones are ok.
* - biz|com|edu|gov|net|org|pro|web|xxx|aero|asia|coop|info|museum|name|shop|рф
* are ok.
* - encoded (`xn--...`) root zones are ok.
*
* If list is replaced, then exact match for 2-chars root zones will be checked.
*
* @param list List of TLDs.
* @param keepOld Merge with current list if `true` (`false` by default).
*/
tlds(list, keepOld = false) {
list = Array.isArray(list) ? list : [list];
if (!keepOld) this.__opts__.tlds = list;
else this.__opts__.tlds = this.__opts__.tlds.concat(list);
this.re.set({
...this.__opts__,
schema_names: Object.keys(this.__schemas__)
});
return this;
}
/**
* Default normalizer (if schema does not define its own).
*
* @param match Match to normalize.
*/
normalize(match) {
if (!match.schema) match.url = `http://${match.url}`;
if (match.schema === "mailto:" && !/^mailto:/i.test(match.url)) match.url = `mailto:${match.url}`;
}
};
function linkifyit(options = {}) {
return new LinkifyIt(options);
}
//#endregion
export { LinkifyIt, REBuilder, linkifyit };
//# sourceMappingURL=index.mjs.map
{"version":3,"file":"index.mjs","names":[],"sources":["../src/rebuilder.ts","../src/linkifyit.ts"],"sourcesContent":["/* eslint-disable no-return-assign, prefer-regex-literals */\n\nimport { Any, Cc, Z, P } from 'uc.micro'\n\n/**\n * @category types\n */\nexport interface REBuilderOptions {\n '---'?: boolean\n fuzzyIP?: boolean\n schema_names?: string[]\n tlds?: string[]\n urlAuth?: boolean\n maxLength?: number\n}\n\nexport class REBuilder {\n src_Any = Any.source\n src_Cc = Cc.source\n src_Z = Z.source\n src_P = P.source\n // \\p{\\Z\\P\\Cc\\CF} (white spaces + control + format + punctuation)\n src_ZPCc = [this.src_Z, this.src_P, this.src_Cc].join('|')\n // \\p{\\Z\\Cc} (white spaces + control)\n src_ZCc = [this.src_Z, this.src_Cc].join('|')\n cache: Record<string, RegExp | undefined> = {}\n opts: REBuilderOptions = { maxLength: 10000, urlAuth: false, schema_names: [] }\n\n constructor (opts: REBuilderOptions = {}) {\n this.opts = { ...this.opts, ...opts }\n }\n\n set (opts: REBuilderOptions = {}) {\n this.opts = { ...this.opts, ...opts }\n\n this.cache = {}\n return this\n }\n\n escapeRE (str: string) { return str.replace(/[.?*+^$[\\]\\\\(){}|-]/g, '\\\\$&') }\n\n nestedPairRE (open: string, close: string, depth = 4) {\n const openRE = this.escapeRE(open)\n const closeRE = this.escapeRE(close)\n const atom = `(?:(?!${this.src_ZCc}|${openRE}|${closeRE}).)`\n\n let pair = `${openRE}${atom}{0,1000}${closeRE}`\n\n for (let level = 2; level <= depth; level++) {\n pair = `${openRE}(?:${atom}|${pair}){0,1000}${closeRE}`\n }\n\n return pair\n }\n\n // Partials\n\n get_text_separators () {\n // Experimental. List of chars, completely prohibited in links\n // because can separate it from other part of text\n return this.cache.text_separators ??= /[><\\uff5c]/\n }\n\n get_pseudo_letter () {\n return this.cache.src_pseudo_letter ??= new RegExp(\n // All possible word characters (everything without punctuation, spaces & controls)\n // Defined via punctuation & spaces to save space\n // Should be something like \\p{\\L\\N\\S\\M} (\\w but without `_`)\n `(?:(?!${this.get_text_separators().source}|${this.src_ZPCc})${this.src_Any})`\n )\n }\n\n get_ipv4_addr () {\n return this.cache.src_ip4 ??= new RegExp(\n '(?:' +\n '(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])[.]' +\n '){3}' +\n '(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])'\n )\n }\n\n get_ipv6_addr () {\n const h16 = '[0-9A-Fa-f]{1,4}'\n const ls32 = `(?:(?:${h16}:${h16})|${this.get_ipv4_addr().source})`\n\n return this.cache.src_ip6_addr ??= new RegExp(\n '(?:' +\n `(?:${h16}:){6}${ls32}|` +\n `::(?:${h16}:){5}${ls32}|` +\n `(?:${h16})?::(?:${h16}:){4}${ls32}|` +\n `(?:(?:${h16}:){0,1}${h16})?::(?:${h16}:){3}${ls32}|` +\n `(?:(?:${h16}:){0,2}${h16})?::(?:${h16}:){2}${ls32}|` +\n `(?:(?:${h16}:){0,3}${h16})?::${h16}:${ls32}|` +\n `(?:(?:${h16}:){0,4}${h16})?::${ls32}|` +\n `(?:(?:${h16}:){0,5}${h16})?::${h16}|` +\n `(?:(?:${h16}:){0,6}${h16})?::` +\n ')'\n )\n }\n\n get_ipv6_url_host () {\n return this.cache.src_ip6_host ??= new RegExp(\n `\\\\[${this.get_ipv6_addr().source}\\\\]`\n )\n }\n\n get_ipv6_mail_host () {\n return this.cache.src_ipv6_mail_host ??= new RegExp(\n `\\\\[IPv6:${this.get_ipv6_addr().source}\\\\]`\n )\n }\n\n get_auth () {\n return this.cache.src_auth ??= new RegExp(\n // Prohibit any of \"@/[]()\" in user/pass to avoid wrong domain fetch.\n // Length is capped to exclude possible rescans till the end and avoid O(n^2)\n // DoS. No standard limit, just take something reasonable.\n `(?:(?:(?!${this.src_ZCc}|[@/\\\\[\\\\]()]).){1,50}@)?`\n )\n }\n\n get_port () {\n return this.cache.src_port ??= new RegExp(\n '(?::(?:6(?:[0-4]\\\\d{3}|5(?:[0-4]\\\\d{2}|5(?:[0-2]\\\\d|3[0-5])))|[1-5]?\\\\d{1,4}))?'\n )\n }\n\n get_host_terminator () {\n // Force greedy fetch, we should not stop earlier than host part is fully fetched.\n return this.cache.src_host_terminator ??= new RegExp(\n `(?=$|${this.get_text_separators().source}|${this.src_ZPCc})` +\n `(?!${this.opts['---'] ? '-(?!--)|' : '-|'}_|:\\\\d|\\\\.-|\\\\.(?!$|${this.src_ZPCc}))`\n )\n }\n\n get_path_terminator () {\n return this.cache.src_path_terminator ??= new RegExp(\n `${this.src_ZPCc}|${this.get_text_separators().source}`\n // `${this.src_ZCc}|${this.get_text_separators().source}|[()[\\\\]{}.,\"'?!\\\\-;]`\n )\n }\n\n get_path () {\n return this.cache.src_path ??= new RegExp(\n '(?:' +\n '[/?#]' +\n '(?:' +\n `${this.nestedPairRE('[', ']')}|` +\n `${this.nestedPairRE('(', ')')}|` +\n `${this.nestedPairRE('{', '}')}|` +\n `\\\\\"(?:(?!${this.src_ZCc}|[\"]).){1,100}\\\\\"|` +\n `\\\\'(?:(?!${this.src_ZCc}|[']).){1,100}\\\\'|` +\n\n // allow `I'm_king` if no pair found\n `\\\\'(?=${this.get_pseudo_letter().source}|[-])|` +\n\n // 1. google has many dots in \"google search\" links (#66, #81).\n // github has ... in commit range links,\n // Restrict to\n // - english\n // - percent-encoded\n // - parts of file path\n // - params separator\n // until more examples found.\n //\n // 2. Allow `..:XX` for Odysee links (optional `:`), #100\n // This may be narrowed down later via separate rule.\n '\\\\.{2,20}[:]?[a-zA-Z0-9%/&]|' +\n\n `\\\\.(?!${this.src_ZCc}|[.]|$)|` +\n (this.opts['---']\n ? '\\\\-(?!--(?:[^-]|$))(?:-{0,19})|' // `---` => long dash, terminate\n : '\\\\-{1,20}|'\n ) +\n // allow `,,,` in paths\n `,(?!${this.src_ZCc}|$)|` +\n\n // allow `;` if not followed by space-like char\n `;(?!${this.src_ZCc}|$)|` +\n\n // allow `!!!` in paths, but not at the end\n `\\\\!{1,20}(?!${this.src_ZCc}|[!]|$)|` +\n\n `\\\\?(?!${this.src_ZCc}|[?]|$)|` +\n\n this.get_path_extra().source +\n\n // allowed punctuation chars in path.\n '[\\\\\\\\/:%@#&=_~*]|' +\n\n // if no special rules matched, consume all chars except terminators.\n `(?!${this.get_path_terminator().source}).` +\n `){1,${this.opts.maxLength}}` +\n '|\\\\/' +\n ')?'\n )\n }\n\n get_mail_name () {\n return this.cache.src_mail_name ??= new RegExp(\n // RFC 5321 dot-string only (no quoted-string), max 64 ASCII characters.\n \"[-!#$%&'*+/=?^_`{|}~a-zA-Z0-9](?:[-!#$%&'*+/=?^_`{|}~a-zA-Z0-9]|[.](?=[-!#$%&'*+/=?^_`{|}~a-zA-Z0-9])){0,63}\"\n )\n }\n\n get_xn () {\n return this.cache.src_xn ??= new RegExp(\n 'xn--[a-z0-9\\\\-]{1,59}'\n )\n }\n\n get_tld () {\n if (this.cache.tld) return this.cache.tld\n\n const tlds_src = [...new Set(this.opts.tlds || [])].sort().reverse()\n .join('|')\n\n this.cache.tld = new RegExp(\n `${tlds_src || '$#none#$'}|${this.get_xn().source}`\n )\n return this.cache.tld\n }\n\n get_domain_root () {\n return this.cache.src_domain_root ??= new RegExp(\n // More to read about domain names\n // http://serverfault.com/questions/638260/\n\n // Allow letters & digits (http://test1)\n '(?:' +\n this.get_xn().source +\n '|' +\n `${this.get_pseudo_letter().source}{1,63}` +\n ')'\n )\n }\n\n get_domain () {\n return this.cache.src_domain ??= new RegExp(\n '(?:' +\n this.get_xn().source +\n '|' +\n `(?:${this.get_pseudo_letter().source})` +\n '|' +\n `(?:${this.get_pseudo_letter().source}(?:-|${this.get_pseudo_letter().source}){0,61}${this.get_pseudo_letter().source})` +\n ')'\n )\n }\n\n // Host rules, depending on the type\n\n get_url_host_port () {\n return this.cache.url_host_port ??= new RegExp(\n '(?:' +\n // Don't need IP v4 check, because digits are already allowed\n // in normal domain names\n this.get_ipv6_url_host().source +\n '|' +\n `(?:(?:(?:${this.get_domain().source})\\\\.){0,10}${this.get_domain().source})`/* _root */ +\n ')' +\n this.get_port().source +\n this.get_host_terminator().source\n )\n }\n\n get_fuzzy_url_host_port () {\n // - TLD as anchor\n // - No local domains\n return this.cache.fuzzy_url_host_port ??= new RegExp(\n '(?:' +\n (this.opts.fuzzyIP ? this.get_ipv4_addr().source + '|' : '') +\n `(?:(?:(?:${this.get_domain().source})\\\\.){1,10}(?:${this.get_tld().source}))` +\n ')' +\n // No port in fuzzy links to reduce search\n this.get_host_terminator().source\n )\n }\n\n get_mail_host () {\n // Similar to normal url host, but\n // - with different ipv6 format (and without port).\n // - with reduced max subdomains\n return this.cache.src_mail_host ??= new RegExp(\n '(?:' +\n this.get_ipv6_mail_host().source +\n '|' +\n `(?:(?:(?:${this.get_domain().source})\\\\.){0,4}${this.get_domain().source})` +\n ')' +\n this.get_host_terminator().source\n )\n }\n\n get_fuzzy_mail_host () {\n // Similar to normal mail host, but without local domains.\n return this.cache.src_fuzzy_mail_host ??= new RegExp(\n '(?:' +\n this.get_ipv6_mail_host().source +\n '|' +\n `(?:(?:(?:${this.get_domain().source})[.]){1,4}${this.get_domain_root().source})` +\n ')' +\n this.get_host_terminator().source\n )\n }\n\n // Hooks\n\n get_path_extra () {\n return this.cache.src_path_extra ??= new RegExp('')\n }\n\n // \"Public\" rules\n\n get_fuzzy_mail_host_search () {\n return this.cache.mail_fuzzy_host_search ??= new RegExp(\n `@${this.get_fuzzy_mail_host().source}`,\n 'ig'\n )\n }\n\n get_fuzzy_link_search () {\n return this.cache.link_fuzzy_search ??= new RegExp(\n // Fuzzy link can't be prepended with .:/\\- and non punctuation.\n // but can start with > (markdown blockquote)\n `(^|(?![.:/\\\\-_@])(?:[$+<=>^\\`|\\uff5c]|${this.src_ZPCc}))` +\n `(?:(?![$+<=>^\\`|\\uff5c])${this.get_fuzzy_url_host_port().source}${this.get_path().source})`,\n 'ig'\n )\n }\n\n get_http_validator () {\n return this.cache.http_validator ??= new RegExp(\n '\\\\/\\\\/' +\n (this.opts.urlAuth ? this.get_auth().source : '') +\n this.get_url_host_port().source +\n this.get_path().source,\n 'iy'\n )\n }\n\n get_relative_proto_validator () {\n return this.cache.relative_proto_validator ??= new RegExp(\n (this.opts.urlAuth ? this.get_auth().source : '') +\n // Don't allow single-level domains, because of false positives like '//test'\n // with code comments.\n `(?:localhost|${this.get_ipv6_url_host().source}|(?:(?:${this.get_domain().source})[.]){1,10}${this.get_domain_root().source})` +\n this.get_port().source +\n this.get_host_terminator().source +\n this.get_path().source,\n\n 'iy'\n )\n }\n\n get_mail_name_validator () {\n return this.cache.mail_name_validator ??= new RegExp(\n `(?:^|${this.get_text_separators().source}|\"|\\\\(|${this.src_ZCc})` +\n `(${this.get_mail_name().source})$`\n )\n }\n\n get_mailto_validator () {\n return this.cache.mailto_validator ??= new RegExp(\n `${this.get_mail_name().source}@${this.get_mail_host().source}`,\n 'iy'\n )\n }\n\n get_schema_names () {\n return this.cache.schema_names ??= new RegExp((this.opts.schema_names || []).map(name => this.escapeRE(name)).join('|'))\n }\n\n get_schema_search () {\n return this.cache.schema_search ??= new RegExp(\n `(^|(?!_)(?:[><\\uff5c]|${this.src_ZPCc}))(${this.get_schema_names().source})`,\n 'ig'\n )\n }\n\n get_schema_at_start () {\n return this.cache.schema_at_start ??= new RegExp(\n `^${this.get_schema_search().source}`,\n 'i'\n )\n }\n}\n","import { REBuilder } from './rebuilder.ts'\n\n//\n\n/**\n * Recognition options for schemaless links.\n *\n * @category types\n */\nexport interface LinkifyOptions {\n /** Recognize URLs without `http(s)://` prefix. Default `false`. */\n fuzzyLink?: boolean\n /** Recognize emails without `mailto:` prefix. Default `true`. */\n fuzzyEmail?: boolean\n /**\n * Allow IPs in fuzzy links. Can conflict with some texts, like version\n * numbers. Default `false`.\n */\n fuzzyIP?: boolean\n /**\n * Terminate link with `---` if it is considered a long dash. Default `false`.\n */\n '---'?: boolean\n /** Allowed TLDs list for fuzzy links. Replaces the default list when set. */\n tlds?: string[]\n /** Recognize authentication data in URLs. Default `false`. */\n urlAuth?: boolean\n /** Maximum link length. Default `10000`. */\n maxLength?: number\n}\n\nexport interface LinkifyConstructorOptions extends LinkifyOptions {\n /** Custom regular expression builder. */\n rebuilder?: REBuilder\n}\n\n/**\n * Custom schema definition.\n *\n * @category types\n */\nexport interface SchemaOpts {\n /**\n * Checks text after the schema prefix. Should return matched tail length on\n * success, or `0` on fail.\n */\n validate: (text: string, pos: number, self: LinkifyIt) => number\n /**\n * Optional function to normalize `text` and `url` of matched result, for\n * example for `@twitter` mentions.\n */\n normalize?: (match: Match, self: LinkifyIt) => void\n}\n\ntype Schema = Required<SchemaOpts>\n\ninterface MatchCandidate {\n schema: string\n index: number\n lastIndex: number\n}\n\nconst web_schema: Schema = {\n validate: (text: string, pos: number, self: LinkifyIt) => {\n const re = self.re.get_http_validator()\n re.lastIndex = pos\n\n const m = re.exec(text)\n return m ? m[0].length : 0\n },\n normalize: (match: Match, self: LinkifyIt) => self.normalize(match)\n}\n\nconst defaultSchemas: Record<string, Schema> = {\n 'http:': web_schema,\n 'https:': web_schema,\n 'ftp:': web_schema,\n '//': {\n validate: function (text: string, pos: number, self: LinkifyIt) {\n const re = self.re.get_relative_proto_validator()\n re.lastIndex = pos\n\n const m = re.exec(text)\n if (m) {\n // should not be `://` & `///`, that protects from errors in protocol name\n if (pos >= 3 && text[pos - 3] === ':') { return 0 }\n if (pos >= 3 && text[pos - 3] === '/') { return 0 }\n return m[0].length\n }\n return 0\n },\n normalize: (match: Match, self: LinkifyIt) => self.normalize(match)\n },\n 'mailto:': {\n validate: function (text: string, pos: number, self: LinkifyIt) {\n const re = self.re.get_mailto_validator()\n re.lastIndex = pos\n\n const m = re.exec(text)\n return m ? m[0].length : 0\n },\n normalize: (match: Match, self: LinkifyIt) => self.normalize(match)\n }\n}\n\n// List of 2-character tlds (autogenerated by ./support/tlds_2char_gen.js)\nconst tlds_2ch = 'a:cdefgilmnoqrstuwxz|b:abdefghijmnorstvwyz|c:acdfghiklmnoruvwxyz|d:ejkmoz|e:cegrstu|f:ijkmor|g:abdefghilmnpqrstuwy|h:kmnrtu|i:delmnoqrst|j:emop|k:eghimnprwyz|l:abcikrstuvy|m:acdeghklmnopqrstuvwxyz|n:acefgilopruz|o:m|p:aefghklmnrstwy|q:a|r:eosuw|s:abcdeghijklmnortuvxyz|t:cdfghjklmnortvwz|u:agksyz|v:aceginu|w:fs|y:et|z:amw'\n\n// DON'T try to make PRs with changes. Extend TLDs with LinkifyIt.tlds() instead\nconst tlds_default = 'biz|com|edu|gov|net|org|pro|web|xxx|aero|asia|coop|info|museum|name|shop|рф'\n\nfunction unpackTlds (): string[] {\n const result = tlds_default.split('|')\n\n tlds_2ch.split('|').forEach((item) => {\n const sep = item.indexOf(':')\n\n const prefix = item.slice(0, sep)\n\n for (const suffix of item.slice(sep + 1)) {\n result.push(prefix + suffix)\n }\n })\n\n return result\n}\n\nconst defaultOptions: Required<LinkifyOptions> = {\n fuzzyLink: false,\n fuzzyEmail: true,\n fuzzyIP: false,\n '---': false,\n tlds: unpackTlds(),\n urlAuth: false,\n maxLength: 10000\n}\n\n/**\n * Match result returned by {@link LinkifyIt.match} and\n * {@link LinkifyIt.matchAtStart}.\n *\n * @category types\n */\nexport class Match {\n /** Prefix (protocol) for matched string. Empty for fuzzy links. */\n schema: string\n /** First position of matched string. */\n index: number\n /** Next position after matched string. */\n lastIndex: number\n /** Matched string. */\n raw: string\n /** Normalized text of matched string. */\n text: string\n /** Normalized URL of matched string. */\n url: string\n\n constructor (text: string, schema: string, index: number, lastIndex: number) {\n const raw = text.slice(index, lastIndex)\n\n this.schema = schema.toLowerCase()\n this.index = index\n this.lastIndex = lastIndex\n this.raw = raw\n this.text = raw\n this.url = raw\n }\n}\n\n/** Linkifier instance. */\nexport class LinkifyIt {\n __opts__: Required<LinkifyOptions>\n private __schemas__: Record<string, Schema>\n re: REBuilder\n\n /**\n * Creates new linkifier instance.\n *\n * By default understands:\n *\n * - `http(s)://...` , `ftp://...`, `mailto:...` & `//...` links\n * - \"fuzzy\" emails (foo@bar.com).\n *\n * See {@link LinkifyConstructorOptions} for available options.\n *\n * @param options Recognition options.\n *\n * @example\n * ```javascript\n * import { LinkifyIt } from 'linkify-it'\n *\n * const linkify = new LinkifyIt({ fuzzyLink: true })\n *\n * linkify\n * .tlds(require('tlds')) // Reload with full TLD list\n * .tlds('onion', true) // Add unofficial `.onion` domain\n * .add('ftp:', null) // Disable `ftp:` protocol\n * .set({ fuzzyIP: true }) // Enable IPs in fuzzy links\n *\n * console.log(linkify.test('Site github.com!')) // true\n * console.log(linkify.match('Site github.com!'))\n * ```\n */\n constructor (options: LinkifyConstructorOptions = {}) {\n const { rebuilder, ...linkifyOptions } = options\n\n this.__opts__ = { ...defaultOptions, ...linkifyOptions }\n\n this.__schemas__ = { ...defaultSchemas }\n\n this.re = rebuilder || new REBuilder()\n this.re.set({\n ...this.__opts__,\n schema_names: Object.keys(this.__schemas__)\n })\n }\n\n /**\n * Add new rule definition.\n *\n * `schema` is a link prefix (usually, protocol name with `:` at the end,\n * `skype:` for example). `linkify-it` makes sure that prefix is not\n * preceded with alphanumeric char and symbols. Only whitespaces and\n * punctuation allowed.\n *\n * `definition` is a rule to check tail after link prefix. To disable an\n * existing rule, pass `null`.\n *\n * @param schema Rule name (fixed pattern prefix).\n * @param definition Schema definition, or `null` to disable the rule.\n *\n * See [twitter mentions example](https://github.com/markdown-it/linkify-it/blob/master/examples/twitter.mjs).\n */\n add (schema: string, definition: SchemaOpts | null = null): this {\n if (!definition) {\n delete this.__schemas__[schema]\n } else {\n const def = {\n normalize: (match: Match, self: LinkifyIt) => self.normalize(match),\n ...definition\n }\n this.__schemas__[schema] = def\n }\n\n this.re.set({\n ...this.__opts__,\n schema_names: Object.keys(this.__schemas__)\n })\n return this\n }\n\n /**\n * Set recognition options for links without schema.\n *\n * @param options Recognition options.\n */\n set (options: LinkifyOptions = {}): this {\n this.__opts__ = { ...this.__opts__, ...options }\n this.re.set({\n ...this.__opts__,\n schema_names: Object.keys(this.__schemas__)\n })\n return this\n }\n\n /**\n * Searches linkifiable pattern and returns `true` on success or `false` on fail.\n *\n * @param text Text to scan.\n */\n test (text: string): boolean {\n if (!text.length) { return false }\n\n let m, re\n\n // try to scan for link with schema - that's the most simple rule\n re = this.re.get_schema_search()\n re.lastIndex = 0\n while ((m = re.exec(text)) !== null) {\n if (this.testSchemaAt(text, m[2], re.lastIndex)) { return true }\n }\n\n if (this.__opts__.fuzzyLink && this.__schemas__['http:']) {\n // guess schemaless links\n re = this.re.get_fuzzy_link_search()\n re.lastIndex = 0\n if (re.exec(text) !== null) { return true }\n }\n\n if (this.__opts__.fuzzyEmail && this.__schemas__['mailto:']) {\n // guess schemaless emails\n if (text.indexOf('@') >= 0) {\n // We can't skip this check, because this cases are possible:\n // 192.168.1.1@gmail.com, my.in@example.com\n const mailHostRe = this.re.get_fuzzy_mail_host_search()\n const mailNameRe = this.re.get_mail_name_validator()\n mailHostRe.lastIndex = 0\n\n while ((m = mailHostRe.exec(text)) !== null) {\n const name = text.slice(Math.max(0, m.index - 65), m.index)\n if (mailNameRe.test(name)) { return true }\n }\n }\n }\n\n return false\n }\n\n /**\n * Similar to {@link LinkifyIt.test} but checks only specific protocol tail exactly\n * at given position. Returns length of found pattern (0 on fail).\n *\n * @param text Text to scan.\n * @param schema Rule (schema) name.\n * @param pos Text offset to check from.\n */\n testSchemaAt (text: string, schema: string, pos: number): number {\n // If not supported schema check requested - terminate\n if (!this.__schemas__[schema.toLowerCase()]) { return 0 }\n\n return this.__schemas__[schema.toLowerCase()].validate(\n text.slice(0, pos + this.__opts__.maxLength),\n pos,\n this\n )\n }\n\n /**\n * Returns array of found link descriptions or `null` on fail. We strongly\n * recommend to use {@link LinkifyIt.test} first, for best speed.\n *\n * @param text Text to scan.\n */\n match (text: string): Match[] | null {\n const result: Match[] = []\n const schemaRe = this.re.get_schema_search()\n let fuzzyLinkRe: RegExp | undefined\n let mailHostRe: RegExp | undefined\n let mailNameRe: RegExp | undefined\n let fuzzyLinkCandidate: MatchCandidate | undefined\n let fuzzyEmailCandidate: MatchCandidate | undefined\n let schemaPrefix: MatchCandidate | undefined\n let schemaDone = false\n let fuzzyLinkDone = false\n let fuzzyEmailDone = false\n let pos = 0\n\n if (!text.length) { return null }\n\n schemaRe.lastIndex = 0\n\n if (this.__opts__.fuzzyLink && this.__schemas__['http:']) {\n fuzzyLinkRe = this.re.get_fuzzy_link_search()\n fuzzyLinkRe.lastIndex = 0\n }\n\n if (this.__opts__.fuzzyEmail && this.__schemas__['mailto:']) {\n mailHostRe = this.re.get_fuzzy_mail_host_search()\n mailHostRe.lastIndex = 0\n mailNameRe = this.re.get_mail_name_validator()\n }\n\n for (;;) {\n const scanFrom = Math.max(pos - 1, 0)\n\n if (mailHostRe && mailNameRe && !fuzzyEmailDone && (!fuzzyEmailCandidate || fuzzyEmailCandidate.index < pos)) {\n if (mailHostRe.lastIndex < scanFrom) { mailHostRe.lastIndex = scanFrom }\n\n for (;;) {\n const m = mailHostRe.exec(text)\n if (!m) {\n fuzzyEmailDone = true\n fuzzyEmailCandidate = undefined\n break\n }\n\n const name = mailNameRe.exec(text.slice(Math.max(0, m.index - 65), m.index))\n if (!name) { continue }\n\n fuzzyEmailCandidate = {\n schema: 'mailto:',\n index: m.index - name[1].length,\n lastIndex: m.index + m[0].length\n }\n\n if (fuzzyEmailCandidate.index >= pos) { break }\n if (mailHostRe.lastIndex < scanFrom) { mailHostRe.lastIndex = scanFrom }\n }\n }\n\n if (fuzzyLinkRe && !fuzzyLinkDone && (!fuzzyLinkCandidate || fuzzyLinkCandidate.index < pos)) {\n if (fuzzyLinkRe.lastIndex < scanFrom) { fuzzyLinkRe.lastIndex = scanFrom }\n\n for (;;) {\n const m = fuzzyLinkRe.exec(text)\n if (!m) {\n fuzzyLinkDone = true\n fuzzyLinkCandidate = undefined\n break\n }\n\n fuzzyLinkCandidate = {\n schema: '',\n index: m.index + m[1].length,\n lastIndex: m.index + m[0].length\n }\n\n if (fuzzyLinkCandidate.index >= pos) { break }\n if (fuzzyLinkRe.lastIndex < scanFrom) { fuzzyLinkRe.lastIndex = scanFrom }\n }\n }\n\n let fuzzyCandidate = fuzzyEmailCandidate\n if (!fuzzyCandidate ||\n (fuzzyLinkCandidate &&\n (fuzzyLinkCandidate.index < fuzzyCandidate.index ||\n (fuzzyLinkCandidate.index === fuzzyCandidate.index && fuzzyLinkCandidate.lastIndex > fuzzyCandidate.lastIndex)))) {\n fuzzyCandidate = fuzzyLinkCandidate\n }\n\n let schemaCandidate: MatchCandidate | undefined\n\n if (!schemaDone) {\n for (;;) {\n if (!schemaPrefix) {\n if (schemaRe.lastIndex < scanFrom) { schemaRe.lastIndex = scanFrom }\n\n const m = schemaRe.exec(text)\n if (!m) {\n schemaDone = true\n break\n }\n\n schemaPrefix = {\n schema: m[2],\n index: m.index + m[1].length,\n lastIndex: m.index + m[0].length\n }\n }\n\n if (schemaPrefix.index < pos) {\n schemaPrefix = undefined\n continue\n }\n\n if (fuzzyCandidate && schemaPrefix.index > fuzzyCandidate.index) { break }\n\n const prefix = schemaPrefix\n schemaPrefix = undefined\n\n const len = this.testSchemaAt(text, prefix.schema, prefix.lastIndex)\n if (len) {\n schemaCandidate = {\n schema: prefix.schema,\n index: prefix.index,\n lastIndex: prefix.lastIndex + len\n }\n break\n }\n }\n }\n\n let candidate = schemaCandidate\n if (!candidate ||\n (fuzzyEmailCandidate &&\n (fuzzyEmailCandidate.index < candidate.index ||\n (fuzzyEmailCandidate.index === candidate.index && fuzzyEmailCandidate.lastIndex > candidate.lastIndex)))) {\n candidate = fuzzyEmailCandidate\n }\n if (!candidate ||\n (fuzzyLinkCandidate &&\n (fuzzyLinkCandidate.index < candidate.index ||\n (fuzzyLinkCandidate.index === candidate.index && fuzzyLinkCandidate.lastIndex > candidate.lastIndex)))) {\n candidate = fuzzyLinkCandidate\n }\n\n if (!candidate) { break }\n\n if (candidate === fuzzyEmailCandidate) {\n fuzzyEmailCandidate = undefined\n } else if (candidate === fuzzyLinkCandidate) {\n fuzzyLinkCandidate = undefined\n }\n\n const match = new Match(text, candidate.schema, candidate.index, candidate.lastIndex)\n if (match.schema) {\n this.__schemas__[match.schema].normalize(match, this)\n } else {\n this.normalize(match)\n }\n result.push(match)\n pos = candidate.lastIndex\n }\n\n if (result.length) {\n return result\n }\n\n return null\n }\n\n /**\n * Returns fully-formed (not fuzzy) link if it starts at the beginning\n * of the string, and null otherwise.\n *\n * @param text Text to scan.\n */\n matchAtStart (text: string): Match | null {\n if (!text.length) return null\n\n const m = this.re.get_schema_at_start().exec(text)\n if (!m) return null\n\n const len = this.testSchemaAt(text, m[2], m[0].length)\n if (!len) return null\n\n const match = new Match(text, m[2], m.index + m[1].length, m.index + m[0].length + len)\n\n this.__schemas__[match.schema].normalize(match, this)\n return match\n }\n\n /**\n * Load (or merge) new TLDs list. Those are used for fuzzy links (without\n * prefix) to avoid false positives. By default this algorithm is used:\n *\n * - hostname with any 2-letter root zones are ok.\n * - biz|com|edu|gov|net|org|pro|web|xxx|aero|asia|coop|info|museum|name|shop|рф\n * are ok.\n * - encoded (`xn--...`) root zones are ok.\n *\n * If list is replaced, then exact match for 2-chars root zones will be checked.\n *\n * @param list List of TLDs.\n * @param keepOld Merge with current list if `true` (`false` by default).\n */\n tlds (list: string | string[], keepOld = false): this {\n list = Array.isArray(list) ? list : [list]\n\n if (!keepOld) {\n this.__opts__.tlds = list\n } else {\n this.__opts__.tlds = this.__opts__.tlds.concat(list)\n }\n\n this.re.set({\n ...this.__opts__,\n schema_names: Object.keys(this.__schemas__)\n })\n return this\n }\n\n /**\n * Default normalizer (if schema does not define its own).\n *\n * @param match Match to normalize.\n */\n normalize (match: Match): void {\n // Do minimal possible changes by default. Need to collect feedback prior\n // to move forward https://github.com/markdown-it/linkify-it/issues/1\n\n if (!match.schema) { match.url = `http://${match.url}` }\n\n if (match.schema === 'mailto:' && !/^mailto:/i.test(match.url)) {\n match.url = `mailto:${match.url}`\n }\n }\n}\n\nfunction linkifyit (options: LinkifyConstructorOptions = {}): LinkifyIt {\n return new LinkifyIt(options)\n}\n\nexport { linkifyit }\n"],"mappings":"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"}
+636
-825

@@ -1,840 +0,651 @@

'use strict';
var uc_micro = require('uc.micro');
function reFactory (opts) {
const re = {};
opts = opts || {};
re.src_Any = uc_micro.Any.source;
re.src_Cc = uc_micro.Cc.source;
re.src_Z = uc_micro.Z.source;
re.src_P = uc_micro.P.source;
// \p{\Z\P\Cc\CF} (white spaces + control + format + punctuation)
re.src_ZPCc = [re.src_Z, re.src_P, re.src_Cc].join('|');
// \p{\Z\Cc} (white spaces + control)
re.src_ZCc = [re.src_Z, re.src_Cc].join('|');
// Experimental. List of chars, completely prohibited in links
// because can separate it from other part of text
const text_separators = '[><\uff5c]';
// All possible word characters (everything without punctuation, spaces & controls)
// Defined via punctuation & spaces to save space
// Should be something like \p{\L\N\S\M} (\w but without `_`)
re.src_pseudo_letter = `(?:(?!${text_separators}|${re.src_ZPCc})${re.src_Any})`;
// The same as abothe but without [0-9]
// var src_pseudo_letter_non_d = '(?:(?![0-9]|' + src_ZPCc + ')' + src_Any + ')';
re.src_ip4 =
'(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)';
// Prohibit any of "@/[]()" in user/pass to avoid wrong domain fetch.
// Length is capped to exclude possible rescans till the end and avoid O(n^2)
// DoS. No standard limit, just take something reasonable.
re.src_auth = `(?:(?:(?!${re.src_ZCc}|[@/\\[\\]()]).){1,50}@)?`;
re.src_port =
'(?::(?:6(?:[0-4]\\d{3}|5(?:[0-4]\\d{2}|5(?:[0-2]\\d|3[0-5])))|[1-5]?\\d{1,4}))?';
re.src_host_terminator =
`(?=$|${text_separators}|${re.src_ZPCc})` +
`(?!${opts['---'] ? '-(?!--)|' : '-|'}_|:\\d|\\.-|\\.(?!$|${re.src_ZPCc}))`;
re.src_path =
'(?:' +
'[/?#]' +
'(?:' +
`(?!${re.src_ZCc}|${text_separators}|[()[\\]{}.,"'?!\\-;]).|` +
`\\[(?:(?!${re.src_ZCc}|\\]).)*\\]|` +
`\\((?:(?!${re.src_ZCc}|[)]).)*\\)|` +
`\\{(?:(?!${re.src_ZCc}|[}]).)*\\}|` +
`\\"(?:(?!${re.src_ZCc}|["]).)+\\"|` +
`\\'(?:(?!${re.src_ZCc}|[']).)+\\'|` +
// allow `I'm_king` if no pair found
`\\'(?=${re.src_pseudo_letter}|[-])|` +
// google has many dots in "google search" links (#66, #81).
// github has ... in commit range links,
// Restrict to
// - english
// - percent-encoded
// - parts of file path
// - params separator
// until more examples found.
'\\.{2,}[a-zA-Z0-9%/&]|' +
`\\.(?!${re.src_ZCc}|[.]|$)|` +
(opts['---']
? '\\-(?!--(?:[^-]|$))(?:-*)|' // `---` => long dash, terminate
: '\\-+|'
) +
// allow `,,,` in paths
`,(?!${re.src_ZCc}|$)|` +
// allow `;` if not followed by space-like char
`;(?!${re.src_ZCc}|$)|` +
// allow `!!!` in paths, but not at the end
`\\!+(?!${re.src_ZCc}|[!]|$)|` +
`\\?(?!${re.src_ZCc}|[?]|$)` +
')+' +
'|\\/' +
')?';
// Allow anything in markdown spec, forbid quote (") at the first position
// because emails enclosed in quotes are far more common
// Max name length capped to 64 chars (RFC 5321). This also prevents O(n^2)
// rescans to the end on inputs like `mailto:mailto:...`
re.src_email_name =
'[\\-;:&=\\+\\$,\\.a-zA-Z0-9_][\\-;:&=\\+\\$,\\"\\.a-zA-Z0-9_]{0,63}';
re.src_xn =
'xn--[a-z0-9\\-]{1,59}';
// More to read about domain names
// http://serverfault.com/questions/638260/
re.src_domain_root =
// Allow letters & digits (http://test1)
'(?:' +
re.src_xn +
'|' +
`${re.src_pseudo_letter}{1,63}` +
')';
re.src_domain =
'(?:' +
re.src_xn +
'|' +
`(?:${re.src_pseudo_letter})` +
'|' +
`(?:${re.src_pseudo_letter}(?:-|${re.src_pseudo_letter}){0,61}${re.src_pseudo_letter})` +
')';
re.src_host =
'(?:' +
// Don't need IP check, because digits are already allowed in normal domain names
// src_ip4 +
// '|' +
`(?:(?:(?:${re.src_domain})\\.)*${re.src_domain})`/* _root */ +
')';
re.tpl_host_fuzzy =
'(?:' +
re.src_ip4 +
'|' +
`(?:(?:(?:${re.src_domain})\\.)+(?:%TLDS%))` +
')';
re.tpl_host_no_ip_fuzzy =
`(?:(?:(?:${re.src_domain})\\.)+(?:%TLDS%))`;
re.src_host_strict =
re.src_host + re.src_host_terminator;
re.tpl_host_fuzzy_strict =
re.tpl_host_fuzzy + re.src_host_terminator;
re.src_host_port_strict =
re.src_host + re.src_port + re.src_host_terminator;
re.tpl_host_port_fuzzy_strict =
re.tpl_host_fuzzy + re.src_port + re.src_host_terminator;
re.tpl_host_port_no_ip_fuzzy_strict =
re.tpl_host_no_ip_fuzzy + re.src_port + re.src_host_terminator;
//
// Main rules
//
// Rude test fuzzy links by host, for quick deny
re.tpl_host_fuzzy_test =
`localhost|www\\.|\\.\\d{1,3}\\.|(?:\\.(?:%TLDS%)(?:${re.src_ZPCc}|>|$))`;
re.tpl_email_fuzzy =
`(^|${text_separators}|"|\\(|${re.src_ZCc})` +
`(${re.src_email_name}@${re.tpl_host_fuzzy_strict})`;
re.tpl_link_fuzzy =
// Fuzzy link can't be prepended with .:/\- and non punctuation.
// but can start with > (markdown blockquote)
`(^|(?![.:/\\-_@])(?:[$+<=>^\`|\uff5c]|${re.src_ZPCc}))` +
`((?![$+<=>^\`|\uff5c])${re.tpl_host_port_fuzzy_strict}${re.src_path})`;
re.tpl_link_no_ip_fuzzy =
// Fuzzy link can't be prepended with .:/\- and non punctuation.
// but can start with > (markdown blockquote)
`(^|(?![.:/\\-_@])(?:[$+<=>^\`|\uff5c]|${re.src_ZPCc}))` +
`((?![$+<=>^\`|\uff5c])${re.tpl_host_port_no_ip_fuzzy_strict}${re.src_path})`;
return re
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
let uc_micro = require("uc.micro");
//#region \0@oxc-project+runtime@0.138.0/helpers/esm/typeof.js
function _typeof(o) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
return typeof o;
} : function(o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof(o);
}
//
// Helpers
//
// Merge objects
//
function assign (obj /* from1, from2, from3, ... */) {
const sources = Array.prototype.slice.call(arguments, 1);
sources.forEach(function (source) {
if (!source) { return }
Object.keys(source).forEach(function (key) {
obj[key] = source[key];
});
});
return obj
//#endregion
//#region \0@oxc-project+runtime@0.138.0/helpers/esm/toPrimitive.js
function toPrimitive(t, r) {
if ("object" != _typeof(t) || !t) return t;
var e = t[Symbol.toPrimitive];
if (void 0 !== e) {
var i = e.call(t, r || "default");
if ("object" != _typeof(i)) return i;
throw new TypeError("@@toPrimitive must return a primitive value.");
}
return ("string" === r ? String : Number)(t);
}
function _class (obj) { return Object.prototype.toString.call(obj) }
function isString (obj) { return _class(obj) === '[object String]' }
function isObject (obj) { return _class(obj) === '[object Object]' }
function isRegExp (obj) { return _class(obj) === '[object RegExp]' }
function isFunction (obj) { return _class(obj) === '[object Function]' }
function escapeRE (str) { return str.replace(/[.?*+^$[\]\\(){}|-]/g, '\\$&') }
//
const defaultOptions = {
fuzzyLink: true,
fuzzyEmail: true,
fuzzyIP: false
};
function isOptionsObj (obj) {
return Object.keys(obj || {}).reduce(function (acc, k) {
/* eslint-disable-next-line no-prototype-builtins */
return acc || defaultOptions.hasOwnProperty(k)
}, false)
//#endregion
//#region \0@oxc-project+runtime@0.138.0/helpers/esm/toPropertyKey.js
function toPropertyKey(t) {
var i = toPrimitive(t, "string");
return "symbol" == _typeof(i) ? i : i + "";
}
const defaultSchemas = {
'http:': {
validate: function (text, pos, self) {
const tail = text.slice(pos);
if (!self.re.http) {
// compile lazily, because "host"-containing variables can change on tlds update.
self.re.http = new RegExp(
`^\\/\\/${self.re.src_auth}${self.re.src_host_port_strict}${self.re.src_path}`, 'i'
);
}
if (self.re.http.test(tail)) {
return tail.match(self.re.http)[0].length
}
return 0
}
},
'https:': 'http:',
'ftp:': 'http:',
'//': {
validate: function (text, pos, self) {
const tail = text.slice(pos);
if (!self.re.no_http) {
// compile lazily, because "host"-containing variables can change on tlds update.
self.re.no_http = new RegExp(
'^' +
self.re.src_auth +
// Don't allow single-level domains, because of false positives like '//test'
// with code comments
`(?:localhost|(?:(?:${self.re.src_domain})\\.)+${self.re.src_domain_root})` +
self.re.src_port +
self.re.src_host_terminator +
self.re.src_path,
'i'
);
}
if (self.re.no_http.test(tail)) {
// should not be `://` & `///`, that protects from errors in protocol name
if (pos >= 3 && text[pos - 3] === ':') { return 0 }
if (pos >= 3 && text[pos - 3] === '/') { return 0 }
return tail.match(self.re.no_http)[0].length
}
return 0
}
},
'mailto:': {
validate: function (text, pos, self) {
const tail = text.slice(pos);
if (!self.re.mailto) {
self.re.mailto = new RegExp(
`^${self.re.src_email_name}@${self.re.src_host_strict}`, 'i'
);
}
if (self.re.mailto.test(tail)) {
return tail.match(self.re.mailto)[0].length
}
return 0
}
}
};
// RE pattern for 2-character tlds (autogenerated by ./support/tlds_2char_gen.js)
const tlds_2ch_src_re = 'a[cdefgilmnoqrstuwxz]|b[abdefghijmnorstvwyz]|c[acdfghiklmnoruvwxyz]|d[ejkmoz]|e[cegrstu]|f[ijkmor]|g[abdefghilmnpqrstuwy]|h[kmnrtu]|i[delmnoqrst]|j[emop]|k[eghimnprwyz]|l[abcikrstuvy]|m[acdeghklmnopqrstuvwxyz]|n[acefgilopruz]|om|p[aefghklmnrstwy]|qa|r[eosuw]|s[abcdeghijklmnortuvxyz]|t[cdfghjklmnortvwz]|u[agksyz]|v[aceginu]|w[fs]|y[et]|z[amw]';
// DON'T try to make PRs with changes. Extend TLDs with LinkifyIt.tlds() instead
const tlds_default = 'biz|com|edu|gov|net|org|pro|web|xxx|aero|asia|coop|info|museum|name|shop|рф'.split('|');
function createValidator (re) {
return function (text, pos) {
const tail = text.slice(pos);
if (re.test(tail)) {
return tail.match(re)[0].length
}
return 0
}
//#endregion
//#region \0@oxc-project+runtime@0.138.0/helpers/esm/defineProperty.js
function _defineProperty(e, r, t) {
return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
value: t,
enumerable: !0,
configurable: !0,
writable: !0
}) : e[r] = t, e;
}
function createNormalizer () {
return function (match, self) {
self.normalize(match);
}
//#endregion
//#region \0@oxc-project+runtime@0.138.0/helpers/esm/objectSpread2.js
function ownKeys(e, r) {
var t = Object.keys(e);
if (Object.getOwnPropertySymbols) {
var o = Object.getOwnPropertySymbols(e);
r && (o = o.filter(function(r) {
return Object.getOwnPropertyDescriptor(e, r).enumerable;
})), t.push.apply(t, o);
}
return t;
}
// Schemas compiler. Build regexps.
//
function compile (self) {
// Load & clone RE patterns.
const re = self.re = reFactory(self.__opts__);
// Define dynamic patterns
const tlds = self.__tlds__.slice();
self.onCompile();
if (!self.__tlds_replaced__) {
tlds.push(tlds_2ch_src_re);
}
tlds.push(re.src_xn);
re.src_tlds = tlds.join('|');
function untpl (tpl) { return tpl.replace('%TLDS%', re.src_tlds) }
re.email_fuzzy = RegExp(untpl(re.tpl_email_fuzzy), 'i');
re.email_fuzzy_global = RegExp(untpl(re.tpl_email_fuzzy), 'ig');
re.link_fuzzy = RegExp(untpl(re.tpl_link_fuzzy), 'i');
re.link_fuzzy_global = RegExp(untpl(re.tpl_link_fuzzy), 'ig');
re.link_no_ip_fuzzy = RegExp(untpl(re.tpl_link_no_ip_fuzzy), 'i');
re.link_no_ip_fuzzy_global = RegExp(untpl(re.tpl_link_no_ip_fuzzy), 'ig');
re.host_fuzzy_test = RegExp(untpl(re.tpl_host_fuzzy_test), 'i');
//
// Compile each schema
//
const aliases = [];
self.__compiled__ = {}; // Reset compiled data
function schemaError (name, val) {
throw new Error(`(LinkifyIt) Invalid schema "${name}": ${val}`)
}
Object.keys(self.__schemas__).forEach(function (name) {
const val = self.__schemas__[name];
// skip disabled methods
if (val === null) { return }
const compiled = { validate: null, link: null };
self.__compiled__[name] = compiled;
if (isObject(val)) {
if (isRegExp(val.validate)) {
compiled.validate = createValidator(val.validate);
} else if (isFunction(val.validate)) {
compiled.validate = val.validate;
} else {
schemaError(name, val);
}
if (isFunction(val.normalize)) {
compiled.normalize = val.normalize;
} else if (!val.normalize) {
compiled.normalize = createNormalizer();
} else {
schemaError(name, val);
}
return
}
if (isString(val)) {
aliases.push(name);
return
}
schemaError(name, val);
});
//
// Compile postponed aliases
//
aliases.forEach(function (alias) {
if (!self.__compiled__[self.__schemas__[alias]]) {
// Silently fail on missed schemas to avoid errons on disable.
// schemaError(alias, self.__schemas__[alias]);
return
}
self.__compiled__[alias].validate =
self.__compiled__[self.__schemas__[alias]].validate;
self.__compiled__[alias].normalize =
self.__compiled__[self.__schemas__[alias]].normalize;
});
//
// Fake record for guessed links
//
self.__compiled__[''] = { validate: null, normalize: createNormalizer() };
//
// Build schema condition
//
const slist = Object.keys(self.__compiled__)
.filter(function (name) {
// Filter disabled & fake schemas
return name.length > 0 && self.__compiled__[name]
})
.map(escapeRE)
.join('|');
// (?!_) cause 1.5x slowdown
self.re.schema_test = RegExp(`(^|(?!_)(?:[><\uff5c]|${re.src_ZPCc}))(${slist})`, 'i');
self.re.schema_search = RegExp(`(^|(?!_)(?:[><\uff5c]|${re.src_ZPCc}))(${slist})`, 'ig');
self.re.schema_at_start = RegExp(`^${self.re.schema_search.source}`, 'i');
self.re.pretest = RegExp(
`(${self.re.schema_test.source})|(${self.re.host_fuzzy_test.source})|@`,
'i'
);
function _objectSpread2(e) {
for (var r = 1; r < arguments.length; r++) {
var t = null != arguments[r] ? arguments[r] : {};
r % 2 ? ownKeys(Object(t), !0).forEach(function(r) {
_defineProperty(e, r, t[r]);
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function(r) {
Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
});
}
return e;
}
/**
* class Match
*
* Match result. Single element of array, returned by [[LinkifyIt#match]]
**/
function Match (text, schema, index, lastIndex) {
const raw = text.slice(index, lastIndex);
/**
* Match#schema -> String
*
* Prefix (protocol) for matched string.
**/
this.schema = schema.toLowerCase();
/**
* Match#index -> Number
*
* First position of matched string.
**/
this.index = index;
/**
* Match#lastIndex -> Number
*
* Next position after matched string.
**/
this.lastIndex = lastIndex;
/**
* Match#raw -> String
*
* Matched string.
**/
this.raw = raw;
/**
* Match#text -> String
*
* Notmalized text of matched string.
**/
this.text = raw;
/**
* Match#url -> String
*
* Normalized url of matched string.
**/
this.url = raw;
//#endregion
//#region src/rebuilder.ts
var REBuilder = class {
constructor(opts = {}) {
_defineProperty(this, "src_Any", uc_micro.Any.source);
_defineProperty(this, "src_Cc", uc_micro.Cc.source);
_defineProperty(this, "src_Z", uc_micro.Z.source);
_defineProperty(this, "src_P", uc_micro.P.source);
_defineProperty(this, "src_ZPCc", [
this.src_Z,
this.src_P,
this.src_Cc
].join("|"));
_defineProperty(this, "src_ZCc", [this.src_Z, this.src_Cc].join("|"));
_defineProperty(this, "cache", {});
_defineProperty(this, "opts", {
maxLength: 1e4,
urlAuth: false,
schema_names: []
});
this.opts = _objectSpread2(_objectSpread2({}, this.opts), opts);
}
set(opts = {}) {
this.opts = _objectSpread2(_objectSpread2({}, this.opts), opts);
this.cache = {};
return this;
}
escapeRE(str) {
return str.replace(/[.?*+^$[\]\\(){}|-]/g, "\\$&");
}
nestedPairRE(open, close, depth = 4) {
const openRE = this.escapeRE(open);
const closeRE = this.escapeRE(close);
const atom = `(?:(?!${this.src_ZCc}|${openRE}|${closeRE}).)`;
let pair = `${openRE}${atom}{0,1000}${closeRE}`;
for (let level = 2; level <= depth; level++) pair = `${openRE}(?:${atom}|${pair}){0,1000}${closeRE}`;
return pair;
}
get_text_separators() {
var _this$cache, _this$cache$text_sepa;
return (_this$cache$text_sepa = (_this$cache = this.cache).text_separators) !== null && _this$cache$text_sepa !== void 0 ? _this$cache$text_sepa : _this$cache.text_separators = /[><\uff5c]/;
}
get_pseudo_letter() {
var _this$cache2, _this$cache2$src_pseu;
return (_this$cache2$src_pseu = (_this$cache2 = this.cache).src_pseudo_letter) !== null && _this$cache2$src_pseu !== void 0 ? _this$cache2$src_pseu : _this$cache2.src_pseudo_letter = new RegExp(`(?:(?!${this.get_text_separators().source}|${this.src_ZPCc})${this.src_Any})`);
}
get_ipv4_addr() {
var _this$cache3, _this$cache3$src_ip;
return (_this$cache3$src_ip = (_this$cache3 = this.cache).src_ip4) !== null && _this$cache3$src_ip !== void 0 ? _this$cache3$src_ip : _this$cache3.src_ip4 = /* @__PURE__ */ new RegExp("(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])[.]){3}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])");
}
get_ipv6_addr() {
var _this$cache4, _this$cache4$src_ip6_;
const h16 = "[0-9A-Fa-f]{1,4}";
const ls32 = `(?:(?:${h16}:${h16})|${this.get_ipv4_addr().source})`;
return (_this$cache4$src_ip6_ = (_this$cache4 = this.cache).src_ip6_addr) !== null && _this$cache4$src_ip6_ !== void 0 ? _this$cache4$src_ip6_ : _this$cache4.src_ip6_addr = new RegExp(`(?:(?:${h16}:){6}${ls32}|::(?:${h16}:){5}${ls32}|(?:${h16})?::(?:${h16}:){4}${ls32}|(?:(?:${h16}:){0,1}${h16})?::(?:${h16}:){3}${ls32}|(?:(?:${h16}:){0,2}${h16})?::(?:${h16}:){2}${ls32}|(?:(?:${h16}:){0,3}${h16})?::${h16}:${ls32}|(?:(?:${h16}:){0,4}${h16})?::${ls32}|(?:(?:${h16}:){0,5}${h16})?::${h16}|(?:(?:${h16}:){0,6}${h16})?::)`);
}
get_ipv6_url_host() {
var _this$cache5, _this$cache5$src_ip6_;
return (_this$cache5$src_ip6_ = (_this$cache5 = this.cache).src_ip6_host) !== null && _this$cache5$src_ip6_ !== void 0 ? _this$cache5$src_ip6_ : _this$cache5.src_ip6_host = new RegExp(`\\[${this.get_ipv6_addr().source}\\]`);
}
get_ipv6_mail_host() {
var _this$cache6, _this$cache6$src_ipv;
return (_this$cache6$src_ipv = (_this$cache6 = this.cache).src_ipv6_mail_host) !== null && _this$cache6$src_ipv !== void 0 ? _this$cache6$src_ipv : _this$cache6.src_ipv6_mail_host = new RegExp(`\\[IPv6:${this.get_ipv6_addr().source}\\]`);
}
get_auth() {
var _this$cache7, _this$cache7$src_auth;
return (_this$cache7$src_auth = (_this$cache7 = this.cache).src_auth) !== null && _this$cache7$src_auth !== void 0 ? _this$cache7$src_auth : _this$cache7.src_auth = new RegExp(`(?:(?:(?!${this.src_ZCc}|[@/\\[\\]()]).){1,50}@)?`);
}
get_port() {
var _this$cache8, _this$cache8$src_port;
return (_this$cache8$src_port = (_this$cache8 = this.cache).src_port) !== null && _this$cache8$src_port !== void 0 ? _this$cache8$src_port : _this$cache8.src_port = /* @__PURE__ */ new RegExp("(?::(?:6(?:[0-4]\\d{3}|5(?:[0-4]\\d{2}|5(?:[0-2]\\d|3[0-5])))|[1-5]?\\d{1,4}))?");
}
get_host_terminator() {
var _this$cache9, _this$cache9$src_host;
return (_this$cache9$src_host = (_this$cache9 = this.cache).src_host_terminator) !== null && _this$cache9$src_host !== void 0 ? _this$cache9$src_host : _this$cache9.src_host_terminator = new RegExp(`(?=$|${this.get_text_separators().source}|${this.src_ZPCc})(?!${this.opts["---"] ? "-(?!--)|" : "-|"}_|:\\d|\\.-|\\.(?!$|${this.src_ZPCc}))`);
}
get_path_terminator() {
var _this$cache10, _this$cache10$src_pat;
return (_this$cache10$src_pat = (_this$cache10 = this.cache).src_path_terminator) !== null && _this$cache10$src_pat !== void 0 ? _this$cache10$src_pat : _this$cache10.src_path_terminator = new RegExp(`${this.src_ZPCc}|${this.get_text_separators().source}`);
}
get_path() {
var _this$cache11, _this$cache11$src_pat;
return (_this$cache11$src_pat = (_this$cache11 = this.cache).src_path) !== null && _this$cache11$src_pat !== void 0 ? _this$cache11$src_pat : _this$cache11.src_path = new RegExp(`(?:[/?#](?:${this.nestedPairRE("[", "]")}|${this.nestedPairRE("(", ")")}|${this.nestedPairRE("{", "}")}|\\"(?:(?!${this.src_ZCc}|["]).){1,100}\\"|\\'(?:(?!${this.src_ZCc}|[']).){1,100}\\'|\\'(?=${this.get_pseudo_letter().source}|[-])|\\.{2,20}[:]?[a-zA-Z0-9%/&]|\\.(?!${this.src_ZCc}|[.]|$)|` + (this.opts["---"] ? "\\-(?!--(?:[^-]|$))(?:-{0,19})|" : "\\-{1,20}|") + `,(?!${this.src_ZCc}|$)|;(?!${this.src_ZCc}|$)|\\!{1,20}(?!${this.src_ZCc}|[!]|$)|\\?(?!${this.src_ZCc}|[?]|$)|` + this.get_path_extra().source + `[\\\\/:%@#&=_~*]|(?!${this.get_path_terminator().source}).){1,${this.opts.maxLength}}|\\/)?`);
}
get_mail_name() {
var _this$cache12, _this$cache12$src_mai;
return (_this$cache12$src_mai = (_this$cache12 = this.cache).src_mail_name) !== null && _this$cache12$src_mai !== void 0 ? _this$cache12$src_mai : _this$cache12.src_mail_name = /* @__PURE__ */ new RegExp("[-!#$%&'*+/=?^_`{|}~a-zA-Z0-9](?:[-!#$%&'*+/=?^_`{|}~a-zA-Z0-9]|[.](?=[-!#$%&'*+/=?^_`{|}~a-zA-Z0-9])){0,63}");
}
get_xn() {
var _this$cache13, _this$cache13$src_xn;
return (_this$cache13$src_xn = (_this$cache13 = this.cache).src_xn) !== null && _this$cache13$src_xn !== void 0 ? _this$cache13$src_xn : _this$cache13.src_xn = /* @__PURE__ */ new RegExp("xn--[a-z0-9\\-]{1,59}");
}
get_tld() {
if (this.cache.tld) return this.cache.tld;
const tlds_src = [...new Set(this.opts.tlds || [])].sort().reverse().join("|");
this.cache.tld = new RegExp(`${tlds_src || "$#none#$"}|${this.get_xn().source}`);
return this.cache.tld;
}
get_domain_root() {
var _this$cache14, _this$cache14$src_dom;
return (_this$cache14$src_dom = (_this$cache14 = this.cache).src_domain_root) !== null && _this$cache14$src_dom !== void 0 ? _this$cache14$src_dom : _this$cache14.src_domain_root = new RegExp("(?:" + this.get_xn().source + `|${this.get_pseudo_letter().source}{1,63})`);
}
get_domain() {
var _this$cache15, _this$cache15$src_dom;
return (_this$cache15$src_dom = (_this$cache15 = this.cache).src_domain) !== null && _this$cache15$src_dom !== void 0 ? _this$cache15$src_dom : _this$cache15.src_domain = new RegExp("(?:" + this.get_xn().source + `|(?:${this.get_pseudo_letter().source})|(?:${this.get_pseudo_letter().source}(?:-|${this.get_pseudo_letter().source}){0,61}${this.get_pseudo_letter().source}))`);
}
get_url_host_port() {
var _this$cache16, _this$cache16$url_hos;
return (_this$cache16$url_hos = (_this$cache16 = this.cache).url_host_port) !== null && _this$cache16$url_hos !== void 0 ? _this$cache16$url_hos : _this$cache16.url_host_port = new RegExp("(?:" + this.get_ipv6_url_host().source + `|(?:(?:(?:${this.get_domain().source})\\.){0,10}${this.get_domain().source}))` + this.get_port().source + this.get_host_terminator().source);
}
get_fuzzy_url_host_port() {
var _this$cache17, _this$cache17$fuzzy_u;
return (_this$cache17$fuzzy_u = (_this$cache17 = this.cache).fuzzy_url_host_port) !== null && _this$cache17$fuzzy_u !== void 0 ? _this$cache17$fuzzy_u : _this$cache17.fuzzy_url_host_port = new RegExp("(?:" + (this.opts.fuzzyIP ? this.get_ipv4_addr().source + "|" : "") + `(?:(?:(?:${this.get_domain().source})\\.){1,10}(?:${this.get_tld().source})))` + this.get_host_terminator().source);
}
get_mail_host() {
var _this$cache18, _this$cache18$src_mai;
return (_this$cache18$src_mai = (_this$cache18 = this.cache).src_mail_host) !== null && _this$cache18$src_mai !== void 0 ? _this$cache18$src_mai : _this$cache18.src_mail_host = new RegExp("(?:" + this.get_ipv6_mail_host().source + `|(?:(?:(?:${this.get_domain().source})\\.){0,4}${this.get_domain().source}))` + this.get_host_terminator().source);
}
get_fuzzy_mail_host() {
var _this$cache19, _this$cache19$src_fuz;
return (_this$cache19$src_fuz = (_this$cache19 = this.cache).src_fuzzy_mail_host) !== null && _this$cache19$src_fuz !== void 0 ? _this$cache19$src_fuz : _this$cache19.src_fuzzy_mail_host = new RegExp("(?:" + this.get_ipv6_mail_host().source + `|(?:(?:(?:${this.get_domain().source})[.]){1,4}${this.get_domain_root().source}))` + this.get_host_terminator().source);
}
get_path_extra() {
var _this$cache20, _this$cache20$src_pat;
return (_this$cache20$src_pat = (_this$cache20 = this.cache).src_path_extra) !== null && _this$cache20$src_pat !== void 0 ? _this$cache20$src_pat : _this$cache20.src_path_extra = /* @__PURE__ */ new RegExp("");
}
get_fuzzy_mail_host_search() {
var _this$cache21, _this$cache21$mail_fu;
return (_this$cache21$mail_fu = (_this$cache21 = this.cache).mail_fuzzy_host_search) !== null && _this$cache21$mail_fu !== void 0 ? _this$cache21$mail_fu : _this$cache21.mail_fuzzy_host_search = new RegExp(`@${this.get_fuzzy_mail_host().source}`, "ig");
}
get_fuzzy_link_search() {
var _this$cache22, _this$cache22$link_fu;
return (_this$cache22$link_fu = (_this$cache22 = this.cache).link_fuzzy_search) !== null && _this$cache22$link_fu !== void 0 ? _this$cache22$link_fu : _this$cache22.link_fuzzy_search = new RegExp(`(^|(?![.:/\\-_@])(?:[$+<=>^\`|\uff5c]|${this.src_ZPCc}))(?:(?![$+<=>^\`|\uff5c])${this.get_fuzzy_url_host_port().source}${this.get_path().source})`, "ig");
}
get_http_validator() {
var _this$cache23, _this$cache23$http_va;
return (_this$cache23$http_va = (_this$cache23 = this.cache).http_validator) !== null && _this$cache23$http_va !== void 0 ? _this$cache23$http_va : _this$cache23.http_validator = new RegExp("\\/\\/" + (this.opts.urlAuth ? this.get_auth().source : "") + this.get_url_host_port().source + this.get_path().source, "iy");
}
get_relative_proto_validator() {
var _this$cache24, _this$cache24$relativ;
return (_this$cache24$relativ = (_this$cache24 = this.cache).relative_proto_validator) !== null && _this$cache24$relativ !== void 0 ? _this$cache24$relativ : _this$cache24.relative_proto_validator = new RegExp((this.opts.urlAuth ? this.get_auth().source : "") + `(?:localhost|${this.get_ipv6_url_host().source}|(?:(?:${this.get_domain().source})[.]){1,10}${this.get_domain_root().source})` + this.get_port().source + this.get_host_terminator().source + this.get_path().source, "iy");
}
get_mail_name_validator() {
var _this$cache25, _this$cache25$mail_na;
return (_this$cache25$mail_na = (_this$cache25 = this.cache).mail_name_validator) !== null && _this$cache25$mail_na !== void 0 ? _this$cache25$mail_na : _this$cache25.mail_name_validator = new RegExp(`(?:^|${this.get_text_separators().source}|"|\\(|${this.src_ZCc})(${this.get_mail_name().source})$`);
}
get_mailto_validator() {
var _this$cache26, _this$cache26$mailto_;
return (_this$cache26$mailto_ = (_this$cache26 = this.cache).mailto_validator) !== null && _this$cache26$mailto_ !== void 0 ? _this$cache26$mailto_ : _this$cache26.mailto_validator = new RegExp(`${this.get_mail_name().source}@${this.get_mail_host().source}`, "iy");
}
get_schema_names() {
var _this$cache27, _this$cache27$schema_;
return (_this$cache27$schema_ = (_this$cache27 = this.cache).schema_names) !== null && _this$cache27$schema_ !== void 0 ? _this$cache27$schema_ : _this$cache27.schema_names = new RegExp((this.opts.schema_names || []).map((name) => this.escapeRE(name)).join("|"));
}
get_schema_search() {
var _this$cache28, _this$cache28$schema_;
return (_this$cache28$schema_ = (_this$cache28 = this.cache).schema_search) !== null && _this$cache28$schema_ !== void 0 ? _this$cache28$schema_ : _this$cache28.schema_search = new RegExp(`(^|(?!_)(?:[><\uff5c]|${this.src_ZPCc}))(${this.get_schema_names().source})`, "ig");
}
get_schema_at_start() {
var _this$cache29, _this$cache29$schema_;
return (_this$cache29$schema_ = (_this$cache29 = this.cache).schema_at_start) !== null && _this$cache29$schema_ !== void 0 ? _this$cache29$schema_ : _this$cache29.schema_at_start = new RegExp(`^${this.get_schema_search().source}`, "i");
}
};
//#endregion
//#region \0@oxc-project+runtime@0.138.0/helpers/esm/objectWithoutPropertiesLoose.js
function _objectWithoutPropertiesLoose(r, e) {
if (null == r) return {};
var t = {};
for (var n in r) if ({}.hasOwnProperty.call(r, n)) {
if (e.includes(n)) continue;
t[n] = r[n];
}
return t;
}
/**
* class LinkifyIt
**/
/**
* new LinkifyIt(schemas, options)
* - schemas (Object): Optional. Additional schemas to validate (prefix/validator)
* - options (Object): { fuzzyLink|fuzzyEmail|fuzzyIP: true|false }
*
* Creates new linkifier instance with optional additional schemas.
* Can be called without `new` keyword for convenience.
*
* By default understands:
*
* - `http(s)://...` , `ftp://...`, `mailto:...` & `//...` links
* - "fuzzy" links and emails (example.com, foo@bar.com).
*
* `schemas` is an object, where each key/value describes protocol/rule:
*
* - __key__ - link prefix (usually, protocol name with `:` at the end, `skype:`
* for example). `linkify-it` makes shure that prefix is not preceeded with
* alphanumeric char and symbols. Only whitespaces and punctuation allowed.
* - __value__ - rule to check tail after link prefix
* - _String_ - just alias to existing rule
* - _Object_
* - _validate_ - validator function (should return matched length on success),
* or `RegExp`.
* - _normalize_ - optional function to normalize text & url of matched result
* (for example, for @twitter mentions).
*
* `options`:
*
* - __fuzzyLink__ - recognige URL-s without `http(s):` prefix. Default `true`.
* - __fuzzyIP__ - allow IPs in fuzzy links above. Can conflict with some texts
* like version numbers. Default `false`.
* - __fuzzyEmail__ - recognize emails without `mailto:` prefix.
*
**/
function LinkifyIt (schemas, options) {
if (!(this instanceof LinkifyIt)) {
return new LinkifyIt(schemas, options)
}
if (!options) {
if (isOptionsObj(schemas)) {
options = schemas;
schemas = {};
}
}
this.__opts__ = assign({}, defaultOptions, options);
this.__schemas__ = assign({}, defaultSchemas, schemas);
this.__compiled__ = {};
this.__tlds__ = tlds_default;
this.__tlds_replaced__ = false;
this.re = {};
compile(this);
//#endregion
//#region \0@oxc-project+runtime@0.138.0/helpers/esm/objectWithoutProperties.js
function _objectWithoutProperties(e, t) {
if (null == e) return {};
var o, r, i = _objectWithoutPropertiesLoose(e, t);
if (Object.getOwnPropertySymbols) {
var s = Object.getOwnPropertySymbols(e);
for (r = 0; r < s.length; r++) o = s[r], t.includes(o) || {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]);
}
return i;
}
/** chainable
* LinkifyIt#add(schema, definition)
* - schema (String): rule name (fixed pattern prefix)
* - definition (String|RegExp|Object): schema definition
*
* Add new rule definition. See constructor description for details.
**/
LinkifyIt.prototype.add = function add (schema, definition) {
this.__schemas__[schema] = definition;
compile(this);
return this
//#endregion
//#region src/linkifyit.ts
var _excluded = ["rebuilder"];
var web_schema = {
validate: (text, pos, self) => {
const re = self.re.get_http_validator();
re.lastIndex = pos;
const m = re.exec(text);
return m ? m[0].length : 0;
},
normalize: (match, self) => self.normalize(match)
};
/** chainable
* LinkifyIt#set(options)
* - options (Object): { fuzzyLink|fuzzyEmail|fuzzyIP: true|false }
*
* Set recognition options for links without schema.
**/
LinkifyIt.prototype.set = function set (options) {
this.__opts__ = assign(this.__opts__, options);
return this
var defaultSchemas = {
"http:": web_schema,
"https:": web_schema,
"ftp:": web_schema,
"//": {
validate: function(text, pos, self) {
const re = self.re.get_relative_proto_validator();
re.lastIndex = pos;
const m = re.exec(text);
if (m) {
if (pos >= 3 && text[pos - 3] === ":") return 0;
if (pos >= 3 && text[pos - 3] === "/") return 0;
return m[0].length;
}
return 0;
},
normalize: (match, self) => self.normalize(match)
},
"mailto:": {
validate: function(text, pos, self) {
const re = self.re.get_mailto_validator();
re.lastIndex = pos;
const m = re.exec(text);
return m ? m[0].length : 0;
},
normalize: (match, self) => self.normalize(match)
}
};
/**
* LinkifyIt#test(text) -> Boolean
*
* Searches linkifiable pattern and returns `true` on success or `false` on fail.
**/
LinkifyIt.prototype.test = function test (text) {
if (!text.length) { return false }
let m, re;
// try to scan for link with schema - that's the most simple rule
if (this.re.schema_test.test(text)) {
re = this.re.schema_search;
re.lastIndex = 0;
while ((m = re.exec(text)) !== null) {
if (this.testSchemaAt(text, m[2], re.lastIndex)) { return true }
}
}
if (this.__opts__.fuzzyLink && this.__compiled__['http:']) {
// guess schemaless links
if (text.search(this.re.host_fuzzy_test) >= 0) {
if (text.match(this.__opts__.fuzzyIP ? this.re.link_fuzzy : this.re.link_no_ip_fuzzy) !== null) {
return true
}
}
}
if (this.__opts__.fuzzyEmail && this.__compiled__['mailto:']) {
// guess schemaless emails
if (text.indexOf('@') >= 0) {
// We can't skip this check, because this cases are possible:
// 192.168.1.1@gmail.com, my.in@example.com
if (text.match(this.re.email_fuzzy) !== null) { return true }
}
}
return false
var tlds_2ch = "a:cdefgilmnoqrstuwxz|b:abdefghijmnorstvwyz|c:acdfghiklmnoruvwxyz|d:ejkmoz|e:cegrstu|f:ijkmor|g:abdefghilmnpqrstuwy|h:kmnrtu|i:delmnoqrst|j:emop|k:eghimnprwyz|l:abcikrstuvy|m:acdeghklmnopqrstuvwxyz|n:acefgilopruz|o:m|p:aefghklmnrstwy|q:a|r:eosuw|s:abcdeghijklmnortuvxyz|t:cdfghjklmnortvwz|u:agksyz|v:aceginu|w:fs|y:et|z:amw";
var tlds_default = "biz|com|edu|gov|net|org|pro|web|xxx|aero|asia|coop|info|museum|name|shop|рф";
function unpackTlds() {
const result = tlds_default.split("|");
tlds_2ch.split("|").forEach((item) => {
const sep = item.indexOf(":");
const prefix = item.slice(0, sep);
for (const suffix of item.slice(sep + 1)) result.push(prefix + suffix);
});
return result;
}
var defaultOptions = {
fuzzyLink: false,
fuzzyEmail: true,
fuzzyIP: false,
"---": false,
tlds: unpackTlds(),
urlAuth: false,
maxLength: 1e4
};
/**
* LinkifyIt#pretest(text) -> Boolean
*
* Very quick check, that can give false positives. Returns true if link MAY BE
* can exists. Can be used for speed optimization, when you need to check that
* link NOT exists.
**/
LinkifyIt.prototype.pretest = function pretest (text) {
return this.re.pretest.test(text)
* Match result returned by {@link LinkifyIt.match} and
* {@link LinkifyIt.matchAtStart}.
*
* @category types
*/
var Match = class {
constructor(text, schema, index, lastIndex) {
_defineProperty(
this,
/** Prefix (protocol) for matched string. Empty for fuzzy links. */
"schema",
void 0
);
_defineProperty(
this,
/** First position of matched string. */
"index",
void 0
);
_defineProperty(
this,
/** Next position after matched string. */
"lastIndex",
void 0
);
_defineProperty(
this,
/** Matched string. */
"raw",
void 0
);
_defineProperty(
this,
/** Normalized text of matched string. */
"text",
void 0
);
_defineProperty(
this,
/** Normalized URL of matched string. */
"url",
void 0
);
const raw = text.slice(index, lastIndex);
this.schema = schema.toLowerCase();
this.index = index;
this.lastIndex = lastIndex;
this.raw = raw;
this.text = raw;
this.url = raw;
}
};
/**
* LinkifyIt#testSchemaAt(text, name, position) -> Number
* - text (String): text to scan
* - name (String): rule (schema) name
* - position (Number): text offset to check from
*
* Similar to [[LinkifyIt#test]] but checks only specific protocol tail exactly
* at given position. Returns length of found pattern (0 on fail).
**/
LinkifyIt.prototype.testSchemaAt = function testSchemaAt (text, schema, pos) {
// If not supported schema check requested - terminate
if (!this.__compiled__[schema.toLowerCase()]) {
return 0
}
return this.__compiled__[schema.toLowerCase()].validate(text, pos, this)
/** Linkifier instance. */
var LinkifyIt = class {
/**
* Creates new linkifier instance.
*
* By default understands:
*
* - `http(s)://...` , `ftp://...`, `mailto:...` & `//...` links
* - "fuzzy" emails (foo@bar.com).
*
* See {@link LinkifyConstructorOptions} for available options.
*
* @param options Recognition options.
*
* @example
* ```javascript
* import { LinkifyIt } from 'linkify-it'
*
* const linkify = new LinkifyIt({ fuzzyLink: true })
*
* linkify
* .tlds(require('tlds')) // Reload with full TLD list
* .tlds('onion', true) // Add unofficial `.onion` domain
* .add('ftp:', null) // Disable `ftp:` protocol
* .set({ fuzzyIP: true }) // Enable IPs in fuzzy links
*
* console.log(linkify.test('Site github.com!')) // true
* console.log(linkify.match('Site github.com!'))
* ```
*/
constructor(options = {}) {
_defineProperty(this, "__opts__", void 0);
_defineProperty(this, "__schemas__", void 0);
_defineProperty(this, "re", void 0);
const { rebuilder } = options, linkifyOptions = _objectWithoutProperties(options, _excluded);
this.__opts__ = _objectSpread2(_objectSpread2({}, defaultOptions), linkifyOptions);
this.__schemas__ = _objectSpread2({}, defaultSchemas);
this.re = rebuilder || new REBuilder();
this.re.set(_objectSpread2(_objectSpread2({}, this.__opts__), {}, { schema_names: Object.keys(this.__schemas__) }));
}
/**
* Add new rule definition.
*
* `schema` is a link prefix (usually, protocol name with `:` at the end,
* `skype:` for example). `linkify-it` makes sure that prefix is not
* preceded with alphanumeric char and symbols. Only whitespaces and
* punctuation allowed.
*
* `definition` is a rule to check tail after link prefix. To disable an
* existing rule, pass `null`.
*
* @param schema Rule name (fixed pattern prefix).
* @param definition Schema definition, or `null` to disable the rule.
*
* See [twitter mentions example](https://github.com/markdown-it/linkify-it/blob/master/examples/twitter.mjs).
*/
add(schema, definition = null) {
if (!definition) delete this.__schemas__[schema];
else {
const def = _objectSpread2({ normalize: (match, self) => self.normalize(match) }, definition);
this.__schemas__[schema] = def;
}
this.re.set(_objectSpread2(_objectSpread2({}, this.__opts__), {}, { schema_names: Object.keys(this.__schemas__) }));
return this;
}
/**
* Set recognition options for links without schema.
*
* @param options Recognition options.
*/
set(options = {}) {
this.__opts__ = _objectSpread2(_objectSpread2({}, this.__opts__), options);
this.re.set(_objectSpread2(_objectSpread2({}, this.__opts__), {}, { schema_names: Object.keys(this.__schemas__) }));
return this;
}
/**
* Searches linkifiable pattern and returns `true` on success or `false` on fail.
*
* @param text Text to scan.
*/
test(text) {
if (!text.length) return false;
let m, re;
re = this.re.get_schema_search();
re.lastIndex = 0;
while ((m = re.exec(text)) !== null) if (this.testSchemaAt(text, m[2], re.lastIndex)) return true;
if (this.__opts__.fuzzyLink && this.__schemas__["http:"]) {
re = this.re.get_fuzzy_link_search();
re.lastIndex = 0;
if (re.exec(text) !== null) return true;
}
if (this.__opts__.fuzzyEmail && this.__schemas__["mailto:"]) {
if (text.indexOf("@") >= 0) {
const mailHostRe = this.re.get_fuzzy_mail_host_search();
const mailNameRe = this.re.get_mail_name_validator();
mailHostRe.lastIndex = 0;
while ((m = mailHostRe.exec(text)) !== null) {
const name = text.slice(Math.max(0, m.index - 65), m.index);
if (mailNameRe.test(name)) return true;
}
}
}
return false;
}
/**
* Similar to {@link LinkifyIt.test} but checks only specific protocol tail exactly
* at given position. Returns length of found pattern (0 on fail).
*
* @param text Text to scan.
* @param schema Rule (schema) name.
* @param pos Text offset to check from.
*/
testSchemaAt(text, schema, pos) {
if (!this.__schemas__[schema.toLowerCase()]) return 0;
return this.__schemas__[schema.toLowerCase()].validate(text.slice(0, pos + this.__opts__.maxLength), pos, this);
}
/**
* Returns array of found link descriptions or `null` on fail. We strongly
* recommend to use {@link LinkifyIt.test} first, for best speed.
*
* @param text Text to scan.
*/
match(text) {
const result = [];
const schemaRe = this.re.get_schema_search();
let fuzzyLinkRe;
let mailHostRe;
let mailNameRe;
let fuzzyLinkCandidate;
let fuzzyEmailCandidate;
let schemaPrefix;
let schemaDone = false;
let fuzzyLinkDone = false;
let fuzzyEmailDone = false;
let pos = 0;
if (!text.length) return null;
schemaRe.lastIndex = 0;
if (this.__opts__.fuzzyLink && this.__schemas__["http:"]) {
fuzzyLinkRe = this.re.get_fuzzy_link_search();
fuzzyLinkRe.lastIndex = 0;
}
if (this.__opts__.fuzzyEmail && this.__schemas__["mailto:"]) {
mailHostRe = this.re.get_fuzzy_mail_host_search();
mailHostRe.lastIndex = 0;
mailNameRe = this.re.get_mail_name_validator();
}
for (;;) {
const scanFrom = Math.max(pos - 1, 0);
if (mailHostRe && mailNameRe && !fuzzyEmailDone && (!fuzzyEmailCandidate || fuzzyEmailCandidate.index < pos)) {
if (mailHostRe.lastIndex < scanFrom) mailHostRe.lastIndex = scanFrom;
for (;;) {
const m = mailHostRe.exec(text);
if (!m) {
fuzzyEmailDone = true;
fuzzyEmailCandidate = void 0;
break;
}
const name = mailNameRe.exec(text.slice(Math.max(0, m.index - 65), m.index));
if (!name) continue;
fuzzyEmailCandidate = {
schema: "mailto:",
index: m.index - name[1].length,
lastIndex: m.index + m[0].length
};
if (fuzzyEmailCandidate.index >= pos) break;
if (mailHostRe.lastIndex < scanFrom) mailHostRe.lastIndex = scanFrom;
}
}
if (fuzzyLinkRe && !fuzzyLinkDone && (!fuzzyLinkCandidate || fuzzyLinkCandidate.index < pos)) {
if (fuzzyLinkRe.lastIndex < scanFrom) fuzzyLinkRe.lastIndex = scanFrom;
for (;;) {
const m = fuzzyLinkRe.exec(text);
if (!m) {
fuzzyLinkDone = true;
fuzzyLinkCandidate = void 0;
break;
}
fuzzyLinkCandidate = {
schema: "",
index: m.index + m[1].length,
lastIndex: m.index + m[0].length
};
if (fuzzyLinkCandidate.index >= pos) break;
if (fuzzyLinkRe.lastIndex < scanFrom) fuzzyLinkRe.lastIndex = scanFrom;
}
}
let fuzzyCandidate = fuzzyEmailCandidate;
if (!fuzzyCandidate || fuzzyLinkCandidate && (fuzzyLinkCandidate.index < fuzzyCandidate.index || fuzzyLinkCandidate.index === fuzzyCandidate.index && fuzzyLinkCandidate.lastIndex > fuzzyCandidate.lastIndex)) fuzzyCandidate = fuzzyLinkCandidate;
let schemaCandidate;
if (!schemaDone) for (;;) {
if (!schemaPrefix) {
if (schemaRe.lastIndex < scanFrom) schemaRe.lastIndex = scanFrom;
const m = schemaRe.exec(text);
if (!m) {
schemaDone = true;
break;
}
schemaPrefix = {
schema: m[2],
index: m.index + m[1].length,
lastIndex: m.index + m[0].length
};
}
if (schemaPrefix.index < pos) {
schemaPrefix = void 0;
continue;
}
if (fuzzyCandidate && schemaPrefix.index > fuzzyCandidate.index) break;
const prefix = schemaPrefix;
schemaPrefix = void 0;
const len = this.testSchemaAt(text, prefix.schema, prefix.lastIndex);
if (len) {
schemaCandidate = {
schema: prefix.schema,
index: prefix.index,
lastIndex: prefix.lastIndex + len
};
break;
}
}
let candidate = schemaCandidate;
if (!candidate || fuzzyEmailCandidate && (fuzzyEmailCandidate.index < candidate.index || fuzzyEmailCandidate.index === candidate.index && fuzzyEmailCandidate.lastIndex > candidate.lastIndex)) candidate = fuzzyEmailCandidate;
if (!candidate || fuzzyLinkCandidate && (fuzzyLinkCandidate.index < candidate.index || fuzzyLinkCandidate.index === candidate.index && fuzzyLinkCandidate.lastIndex > candidate.lastIndex)) candidate = fuzzyLinkCandidate;
if (!candidate) break;
if (candidate === fuzzyEmailCandidate) fuzzyEmailCandidate = void 0;
else if (candidate === fuzzyLinkCandidate) fuzzyLinkCandidate = void 0;
const match = new Match(text, candidate.schema, candidate.index, candidate.lastIndex);
if (match.schema) this.__schemas__[match.schema].normalize(match, this);
else this.normalize(match);
result.push(match);
pos = candidate.lastIndex;
}
if (result.length) return result;
return null;
}
/**
* Returns fully-formed (not fuzzy) link if it starts at the beginning
* of the string, and null otherwise.
*
* @param text Text to scan.
*/
matchAtStart(text) {
if (!text.length) return null;
const m = this.re.get_schema_at_start().exec(text);
if (!m) return null;
const len = this.testSchemaAt(text, m[2], m[0].length);
if (!len) return null;
const match = new Match(text, m[2], m.index + m[1].length, m.index + m[0].length + len);
this.__schemas__[match.schema].normalize(match, this);
return match;
}
/**
* Load (or merge) new TLDs list. Those are used for fuzzy links (without
* prefix) to avoid false positives. By default this algorithm is used:
*
* - hostname with any 2-letter root zones are ok.
* - biz|com|edu|gov|net|org|pro|web|xxx|aero|asia|coop|info|museum|name|shop|рф
* are ok.
* - encoded (`xn--...`) root zones are ok.
*
* If list is replaced, then exact match for 2-chars root zones will be checked.
*
* @param list List of TLDs.
* @param keepOld Merge with current list if `true` (`false` by default).
*/
tlds(list, keepOld = false) {
list = Array.isArray(list) ? list : [list];
if (!keepOld) this.__opts__.tlds = list;
else this.__opts__.tlds = this.__opts__.tlds.concat(list);
this.re.set(_objectSpread2(_objectSpread2({}, this.__opts__), {}, { schema_names: Object.keys(this.__schemas__) }));
return this;
}
/**
* Default normalizer (if schema does not define its own).
*
* @param match Match to normalize.
*/
normalize(match) {
if (!match.schema) match.url = `http://${match.url}`;
if (match.schema === "mailto:" && !/^mailto:/i.test(match.url)) match.url = `mailto:${match.url}`;
}
};
function linkifyit(options = {}) {
return new LinkifyIt(options);
}
//#endregion
exports.LinkifyIt = LinkifyIt;
exports.REBuilder = REBuilder;
exports.linkifyit = linkifyit;
/**
* LinkifyIt#match(text) -> Array|null
*
* Returns array of found link descriptions or `null` on fail. We strongly
* recommend to use [[LinkifyIt#test]] first, for best speed.
*
* ##### Result match description
*
* - __schema__ - link schema, can be empty for fuzzy links, or `//` for
* protocol-neutral links.
* - __index__ - offset of matched text
* - __lastIndex__ - index of next char after mathch end
* - __raw__ - matched text
* - __text__ - normalized text
* - __url__ - link, generated from matched text
**/
LinkifyIt.prototype.match = function match (text) {
const result = [];
const type_schemed = [];
const type_fuzzy_link = [];
const type_fuzzy_email = [];
let m, len, re;
function choose (a, b) {
if (!a) { return b }
if (!b) { return a }
if (a.index !== b.index) { return a.index < b.index ? a : b }
return a.lastIndex >= b.lastIndex ? a : b
}
if (!text.length) { return null }
// scan for links with schema
if (this.re.schema_test.test(text)) {
re = this.re.schema_search;
re.lastIndex = 0;
while ((m = re.exec(text)) !== null) {
len = this.testSchemaAt(text, m[2], re.lastIndex);
if (len) {
type_schemed.push({
schema: m[2],
index: m.index + m[1].length,
lastIndex: m.index + m[0].length + len
});
}
}
}
if (this.__opts__.fuzzyLink && this.__compiled__['http:']) {
re = this.__opts__.fuzzyIP ? this.re.link_fuzzy_global : this.re.link_no_ip_fuzzy_global;
re.lastIndex = 0;
while ((m = re.exec(text)) !== null) {
type_fuzzy_link.push({
schema: '',
index: m.index + m[1].length,
lastIndex: m.index + m[0].length
});
}
}
if (this.__opts__.fuzzyEmail && this.__compiled__['mailto:']) {
re = this.re.email_fuzzy_global;
re.lastIndex = 0;
while ((m = re.exec(text)) !== null) {
type_fuzzy_email.push({
schema: 'mailto:',
index: m.index + m[1].length,
lastIndex: m.index + m[0].length
});
}
}
const indexes = [0, 0, 0];
let lastIndex = 0;
for (;;) {
const candidates = [
type_schemed[indexes[0]],
type_fuzzy_email[indexes[1]],
type_fuzzy_link[indexes[2]]
];
const candidate = choose(choose(candidates[0], candidates[1]), candidates[2]);
if (!candidate) { break }
if (candidate === candidates[0]) {
indexes[0]++;
} else if (candidate === candidates[1]) {
indexes[1]++;
} else {
indexes[2]++;
}
if (candidate.index < lastIndex) { continue }
const match = new Match(text, candidate.schema, candidate.index, candidate.lastIndex);
this.__compiled__[match.schema].normalize(match, this);
result.push(match);
lastIndex = candidate.lastIndex;
}
if (result.length) {
return result
}
return null
};
/**
* LinkifyIt#matchAtStart(text) -> Match|null
*
* Returns fully-formed (not fuzzy) link if it starts at the beginning
* of the string, and null otherwise.
**/
LinkifyIt.prototype.matchAtStart = function matchAtStart (text) {
if (!text.length) return null
const m = this.re.schema_at_start.exec(text);
if (!m) return null
const len = this.testSchemaAt(text, m[2], m[0].length);
if (!len) return null
const match = new Match(text, m[2], m.index + m[1].length, m.index + m[0].length + len);
this.__compiled__[match.schema].normalize(match, this);
return match
};
/** chainable
* LinkifyIt#tlds(list [, keepOld]) -> this
* - list (Array): list of tlds
* - keepOld (Boolean): merge with current list if `true` (`false` by default)
*
* Load (or merge) new tlds list. Those are user for fuzzy links (without prefix)
* to avoid false positives. By default this algorythm used:
*
* - hostname with any 2-letter root zones are ok.
* - biz|com|edu|gov|net|org|pro|web|xxx|aero|asia|coop|info|museum|name|shop|рф
* are ok.
* - encoded (`xn--...`) root zones are ok.
*
* If list is replaced, then exact match for 2-chars root zones will be checked.
**/
LinkifyIt.prototype.tlds = function tlds (list, keepOld) {
list = Array.isArray(list) ? list : [list];
if (!keepOld) {
this.__tlds__ = list.slice();
this.__tlds_replaced__ = true;
compile(this);
return this
}
this.__tlds__ = this.__tlds__.concat(list)
.sort()
.filter(function (el, idx, arr) {
return el !== arr[idx - 1]
})
.reverse();
compile(this);
return this
};
/**
* LinkifyIt#normalize(match)
*
* Default normalizer (if schema does not define it's own).
**/
LinkifyIt.prototype.normalize = function normalize (match) {
// Do minimal possible changes by default. Need to collect feedback prior
// to move forward https://github.com/markdown-it/linkify-it/issues/1
if (!match.schema) { match.url = `http://${match.url}`; }
if (match.schema === 'mailto:' && !/^mailto:/i.test(match.url)) {
match.url = `mailto:${match.url}`;
}
};
/**
* LinkifyIt#onCompile()
*
* Override to modify basic RegExp-s.
**/
LinkifyIt.prototype.onCompile = function onCompile () {
};
module.exports = LinkifyIt;
//# sourceMappingURL=index.cjs.js.map
{
"name": "linkify-it",
"version": "5.0.2",
"version": "6.0.0",
"description": "Links recognition library with FULL unicode support",

@@ -13,16 +13,13 @@ "keywords": [

"main": "build/index.cjs.js",
"module": "index.mjs",
"module": "build/index.mjs",
"types": "build/index.d.ts",
"exports": {
".": {
"types": "./build/index.d.ts",
"require": "./build/index.cjs.js",
"import": "./index.mjs"
"import": "./build/index.mjs"
},
"./*": {
"require": "./*",
"import": "./*"
}
"./package.json": "./package.json"
},
"files": [
"index.mjs",
"lib/",
"build/"

@@ -43,8 +40,8 @@ ],

"lint": "eslint .",
"test": "npm run lint && npm run build && c8 --exclude build --exclude test -r text -r html -r lcov node --test",
"demo": "npm run lint && node support/build_demo.mjs",
"doc": "node support/build_doc.mjs",
"build": "rollup -c support/rollup.config.mjs",
"gh-pages": "npm run demo && npm run doc && gh-pages -d demo -f",
"prepack": "npm test && npm run build && npm run demo && npm run doc",
"type-check": "tsc --noEmit",
"test": "npm run lint && npm run build && npm run type-check && c8 --exclude build --exclude test -r text -r html -r lcov node --test",
"doc": "typedoc",
"build": "node support/build_dist.mjs && node support/build_demo.mjs",
"gh-pages": "npm run build && npm run doc && gh-pages -d demo -f",
"prepack": "npm test && npm run doc",
"postpublish": "npm run gh-pages"

@@ -56,3 +53,2 @@ },

"devDependencies": {
"@rollup/plugin-node-resolve": "^15.2.3",
"c8": "^11.0.0",

@@ -63,5 +59,9 @@ "eslint": "^9.39.3",

"neostandard": "^0.13.0",
"ndoc": "^6.0.0",
"rollup": "^4.6.1"
"rollup": "^4.62.2",
"rollup-plugin-dts": "^6.4.1",
"typedoc": "^0.28.19",
"typescript": "^6.0.3",
"vite": "^8.1.2",
"vite-plugin-singlefile": "^2.3.3"
}
}
+47
-84

@@ -27,63 +27,37 @@ linkify-it

Browserification is also supported.
Usage examples
--------------
##### Example 1
```js
import linkifyit from 'linkify-it';
const linkify = linkifyit();
import { LinkifyIt } from 'linkify-it';
const linkify = new LinkifyIt({ fuzzyLink: true });
// Reload full tlds list & add unofficial `.onion` domain.
linkify
.tlds(require('tlds')) // Reload with full tlds list
.tlds('onion', true) // Add unofficial `.onion` domain
.add('git:', 'http:') // Add `git:` protocol as "alias"
.add('ftp:', null) // Disable `ftp:` protocol
.set({ fuzzyIP: true }); // Enable IPs in fuzzy links (without schema)
.tlds(require('tlds'))
.tlds('onion', true)
.add('ftp:', null)
.set({ fuzzyIP: true });
console.log(linkify.test('Site github.com!')); // true
console.log(linkify.test('Site github.com!'));
// true
console.log(linkify.match('Site github.com!')); // [ {
// schema: "",
// index: 5,
// lastIndex: 15,
// raw: "github.com",
// text: "github.com",
// url: "http://github.com",
// } ]
console.log(linkify.match('Site github.com!'));
// [ {
// schema: "",
// index: 5,
// lastIndex: 15,
// raw: "github.com",
// text: "github.com",
// url: "http://github.com",
// } ]
```
##### Example 2. Add twitter mentions handler
See more in examples folder:
```js
linkify.add('@', {
validate: function (text, pos, self) {
const tail = text.slice(pos);
- [twitter mentions](examples/twitter.mjs)
- [CJK paired brackets in URL paths](examples/cjk-paired-brackets.mjs)
- [increased nested scopes depth](examples/nested-scopes-depth.mjs)
if (!self.re.twitter) {
self.re.twitter = new RegExp(
`^([a-zA-Z0-9_]){1,15}(?!_)(?=$|${self.re.src_ZPCc})`
);
}
if (self.re.twitter.test(tail)) {
// Linkifier allows punctuation chars before prefix,
// but we additionally disable `@` ("@@mention" is invalid)
if (pos >= 2 && tail[pos - 2] === '@') {
return false;
}
return tail.match(self.re.twitter)[0].length;
}
return 0;
},
normalize: function (match) {
match.url = `https://twitter.com/${match.url.replace(/^@/, '')}`;
}
});
```
API

@@ -94,6 +68,5 @@ ---

### new LinkifyIt(schemas, options)
### new LinkifyIt(options)
Creates new linkifier instance with optional additional schemas.
Can be called without `new` keyword for convenience.
Creates new linkifier instance.

@@ -103,27 +76,17 @@ By default understands:

- `http(s)://...` , `ftp://...`, `mailto:...` & `//...` links
- "fuzzy" links and emails (google.com, foo@bar.com).
- "fuzzy" emails (foo@bar.com).
`schemas` is an object, where each key/value describes protocol/rule:
- __key__ - link prefix (usually, protocol name with `:` at the end, `skype:`
for example). `linkify-it` makes sure that prefix is not preceded with
alphanumeric char.
- __value__ - rule to check tail after link prefix
- _String_ - just alias to existing rule
- _Object_
- _validate_ - either a `RegExp` (start with `^`, and don't include the
link prefix itself), or a validator function which, given arguments
_text_, _pos_, and _self_, returns the length of a match in _text_
starting at index _pos_. _pos_ is the index right after the link prefix.
_self_ can be used to access the linkify object to cache data.
- _normalize_ - optional function to normalize text & url of matched result
(for example, for twitter mentions).
`options`:
- __fuzzyLink__ - recognize URL-s without `http(s)://` head. Default `true`.
- __fuzzyLink__ - recognize URL-s without `http(s)://` head. Default `false`.
- __fuzzyIP__ - allow IPs in fuzzy links above. Can conflict with some texts
like version numbers. Default `false`.
- __fuzzyEmail__ - recognize emails without `mailto:` prefix. Default `true`.
- __tlds__ - allowed TLDs list for fuzzy links. Replaces the default list
when set.
- __---__ - set `true` to terminate link with `---` (if it's considered as long dash).
Default `false`.
- __rebuilder__ - custom `REBuilder` instance for patched regex fragments.
- __urlAuth__ - recognize authentication data in URLs. Default `false`.
- __maxLength__ - maximum link length. Default `10000`.

@@ -136,9 +99,2 @@

### .pretest(text)
Quick check if link MAY BE can exist. Can be used to optimize more expensive
`.test()` calls. Return `false` if link can not be found, `true` - if `.test()`
call needed to know exactly.
### .testSchemaAt(text, name, offset)

@@ -185,16 +141,23 @@

Add a new schema to the schemas object. As described in the constructor
definition, `key` is a link prefix (`skype:`, for example), and `value`
is a String to alias to another schema, or an Object with `validate` and
optionally `normalize` definitions. To disable an existing rule, use
`.add(key, null)`.
Add a new schema to the schemas object.
`key` is a link prefix (usually, protocol name with `:` at the end, `skype:`
for example). `linkify-it` makes sure that prefix is not preceded with
alphanumeric char.
### .set(options)
`value` is a rule to check tail after link prefix:
Override default options. Missed properties will not be changed.
- _Object_
- _validate_ - validator function which, given arguments _text_, _pos_, and
_self_, returns the length of a match in _text_ starting at index _pos_.
_pos_ is the index right after the link prefix. _self_ can be used to
access the linkify object to cache data.
- _normalize_ - optional function to normalize text & url of matched result
(for example, for twitter mentions).
To disable an existing rule, use `.add(key, null)`.
## License
[MIT](https://github.com/markdown-it/linkify-it/blob/master/LICENSE)
### .set(options)
Override default options. Missed properties will not be changed.
import reFactory from './lib/re.mjs'
//
// Helpers
//
// Merge objects
//
function assign (obj /* from1, from2, from3, ... */) {
const sources = Array.prototype.slice.call(arguments, 1)
sources.forEach(function (source) {
if (!source) { return }
Object.keys(source).forEach(function (key) {
obj[key] = source[key]
})
})
return obj
}
function _class (obj) { return Object.prototype.toString.call(obj) }
function isString (obj) { return _class(obj) === '[object String]' }
function isObject (obj) { return _class(obj) === '[object Object]' }
function isRegExp (obj) { return _class(obj) === '[object RegExp]' }
function isFunction (obj) { return _class(obj) === '[object Function]' }
function escapeRE (str) { return str.replace(/[.?*+^$[\]\\(){}|-]/g, '\\$&') }
//
const defaultOptions = {
fuzzyLink: true,
fuzzyEmail: true,
fuzzyIP: false
}
function isOptionsObj (obj) {
return Object.keys(obj || {}).reduce(function (acc, k) {
/* eslint-disable-next-line no-prototype-builtins */
return acc || defaultOptions.hasOwnProperty(k)
}, false)
}
const defaultSchemas = {
'http:': {
validate: function (text, pos, self) {
const tail = text.slice(pos)
if (!self.re.http) {
// compile lazily, because "host"-containing variables can change on tlds update.
self.re.http = new RegExp(
`^\\/\\/${self.re.src_auth}${self.re.src_host_port_strict}${self.re.src_path}`, 'i'
)
}
if (self.re.http.test(tail)) {
return tail.match(self.re.http)[0].length
}
return 0
}
},
'https:': 'http:',
'ftp:': 'http:',
'//': {
validate: function (text, pos, self) {
const tail = text.slice(pos)
if (!self.re.no_http) {
// compile lazily, because "host"-containing variables can change on tlds update.
self.re.no_http = new RegExp(
'^' +
self.re.src_auth +
// Don't allow single-level domains, because of false positives like '//test'
// with code comments
`(?:localhost|(?:(?:${self.re.src_domain})\\.)+${self.re.src_domain_root})` +
self.re.src_port +
self.re.src_host_terminator +
self.re.src_path,
'i'
)
}
if (self.re.no_http.test(tail)) {
// should not be `://` & `///`, that protects from errors in protocol name
if (pos >= 3 && text[pos - 3] === ':') { return 0 }
if (pos >= 3 && text[pos - 3] === '/') { return 0 }
return tail.match(self.re.no_http)[0].length
}
return 0
}
},
'mailto:': {
validate: function (text, pos, self) {
const tail = text.slice(pos)
if (!self.re.mailto) {
self.re.mailto = new RegExp(
`^${self.re.src_email_name}@${self.re.src_host_strict}`, 'i'
)
}
if (self.re.mailto.test(tail)) {
return tail.match(self.re.mailto)[0].length
}
return 0
}
}
}
// RE pattern for 2-character tlds (autogenerated by ./support/tlds_2char_gen.js)
const tlds_2ch_src_re = 'a[cdefgilmnoqrstuwxz]|b[abdefghijmnorstvwyz]|c[acdfghiklmnoruvwxyz]|d[ejkmoz]|e[cegrstu]|f[ijkmor]|g[abdefghilmnpqrstuwy]|h[kmnrtu]|i[delmnoqrst]|j[emop]|k[eghimnprwyz]|l[abcikrstuvy]|m[acdeghklmnopqrstuvwxyz]|n[acefgilopruz]|om|p[aefghklmnrstwy]|qa|r[eosuw]|s[abcdeghijklmnortuvxyz]|t[cdfghjklmnortvwz]|u[agksyz]|v[aceginu]|w[fs]|y[et]|z[amw]'
// DON'T try to make PRs with changes. Extend TLDs with LinkifyIt.tlds() instead
const tlds_default = 'biz|com|edu|gov|net|org|pro|web|xxx|aero|asia|coop|info|museum|name|shop|рф'.split('|')
function createValidator (re) {
return function (text, pos) {
const tail = text.slice(pos)
if (re.test(tail)) {
return tail.match(re)[0].length
}
return 0
}
}
function createNormalizer () {
return function (match, self) {
self.normalize(match)
}
}
// Schemas compiler. Build regexps.
//
function compile (self) {
// Load & clone RE patterns.
const re = self.re = reFactory(self.__opts__)
// Define dynamic patterns
const tlds = self.__tlds__.slice()
self.onCompile()
if (!self.__tlds_replaced__) {
tlds.push(tlds_2ch_src_re)
}
tlds.push(re.src_xn)
re.src_tlds = tlds.join('|')
function untpl (tpl) { return tpl.replace('%TLDS%', re.src_tlds) }
re.email_fuzzy = RegExp(untpl(re.tpl_email_fuzzy), 'i')
re.email_fuzzy_global = RegExp(untpl(re.tpl_email_fuzzy), 'ig')
re.link_fuzzy = RegExp(untpl(re.tpl_link_fuzzy), 'i')
re.link_fuzzy_global = RegExp(untpl(re.tpl_link_fuzzy), 'ig')
re.link_no_ip_fuzzy = RegExp(untpl(re.tpl_link_no_ip_fuzzy), 'i')
re.link_no_ip_fuzzy_global = RegExp(untpl(re.tpl_link_no_ip_fuzzy), 'ig')
re.host_fuzzy_test = RegExp(untpl(re.tpl_host_fuzzy_test), 'i')
//
// Compile each schema
//
const aliases = []
self.__compiled__ = {} // Reset compiled data
function schemaError (name, val) {
throw new Error(`(LinkifyIt) Invalid schema "${name}": ${val}`)
}
Object.keys(self.__schemas__).forEach(function (name) {
const val = self.__schemas__[name]
// skip disabled methods
if (val === null) { return }
const compiled = { validate: null, link: null }
self.__compiled__[name] = compiled
if (isObject(val)) {
if (isRegExp(val.validate)) {
compiled.validate = createValidator(val.validate)
} else if (isFunction(val.validate)) {
compiled.validate = val.validate
} else {
schemaError(name, val)
}
if (isFunction(val.normalize)) {
compiled.normalize = val.normalize
} else if (!val.normalize) {
compiled.normalize = createNormalizer()
} else {
schemaError(name, val)
}
return
}
if (isString(val)) {
aliases.push(name)
return
}
schemaError(name, val)
})
//
// Compile postponed aliases
//
aliases.forEach(function (alias) {
if (!self.__compiled__[self.__schemas__[alias]]) {
// Silently fail on missed schemas to avoid errons on disable.
// schemaError(alias, self.__schemas__[alias]);
return
}
self.__compiled__[alias].validate =
self.__compiled__[self.__schemas__[alias]].validate
self.__compiled__[alias].normalize =
self.__compiled__[self.__schemas__[alias]].normalize
})
//
// Fake record for guessed links
//
self.__compiled__[''] = { validate: null, normalize: createNormalizer() }
//
// Build schema condition
//
const slist = Object.keys(self.__compiled__)
.filter(function (name) {
// Filter disabled & fake schemas
return name.length > 0 && self.__compiled__[name]
})
.map(escapeRE)
.join('|')
// (?!_) cause 1.5x slowdown
self.re.schema_test = RegExp(`(^|(?!_)(?:[><\uff5c]|${re.src_ZPCc}))(${slist})`, 'i')
self.re.schema_search = RegExp(`(^|(?!_)(?:[><\uff5c]|${re.src_ZPCc}))(${slist})`, 'ig')
self.re.schema_at_start = RegExp(`^${self.re.schema_search.source}`, 'i')
self.re.pretest = RegExp(
`(${self.re.schema_test.source})|(${self.re.host_fuzzy_test.source})|@`,
'i'
)
}
/**
* class Match
*
* Match result. Single element of array, returned by [[LinkifyIt#match]]
**/
function Match (text, schema, index, lastIndex) {
const raw = text.slice(index, lastIndex)
/**
* Match#schema -> String
*
* Prefix (protocol) for matched string.
**/
this.schema = schema.toLowerCase()
/**
* Match#index -> Number
*
* First position of matched string.
**/
this.index = index
/**
* Match#lastIndex -> Number
*
* Next position after matched string.
**/
this.lastIndex = lastIndex
/**
* Match#raw -> String
*
* Matched string.
**/
this.raw = raw
/**
* Match#text -> String
*
* Notmalized text of matched string.
**/
this.text = raw
/**
* Match#url -> String
*
* Normalized url of matched string.
**/
this.url = raw
}
/**
* class LinkifyIt
**/
/**
* new LinkifyIt(schemas, options)
* - schemas (Object): Optional. Additional schemas to validate (prefix/validator)
* - options (Object): { fuzzyLink|fuzzyEmail|fuzzyIP: true|false }
*
* Creates new linkifier instance with optional additional schemas.
* Can be called without `new` keyword for convenience.
*
* By default understands:
*
* - `http(s)://...` , `ftp://...`, `mailto:...` & `//...` links
* - "fuzzy" links and emails (example.com, foo@bar.com).
*
* `schemas` is an object, where each key/value describes protocol/rule:
*
* - __key__ - link prefix (usually, protocol name with `:` at the end, `skype:`
* for example). `linkify-it` makes shure that prefix is not preceeded with
* alphanumeric char and symbols. Only whitespaces and punctuation allowed.
* - __value__ - rule to check tail after link prefix
* - _String_ - just alias to existing rule
* - _Object_
* - _validate_ - validator function (should return matched length on success),
* or `RegExp`.
* - _normalize_ - optional function to normalize text & url of matched result
* (for example, for @twitter mentions).
*
* `options`:
*
* - __fuzzyLink__ - recognige URL-s without `http(s):` prefix. Default `true`.
* - __fuzzyIP__ - allow IPs in fuzzy links above. Can conflict with some texts
* like version numbers. Default `false`.
* - __fuzzyEmail__ - recognize emails without `mailto:` prefix.
*
**/
function LinkifyIt (schemas, options) {
if (!(this instanceof LinkifyIt)) {
return new LinkifyIt(schemas, options)
}
if (!options) {
if (isOptionsObj(schemas)) {
options = schemas
schemas = {}
}
}
this.__opts__ = assign({}, defaultOptions, options)
this.__schemas__ = assign({}, defaultSchemas, schemas)
this.__compiled__ = {}
this.__tlds__ = tlds_default
this.__tlds_replaced__ = false
this.re = {}
compile(this)
}
/** chainable
* LinkifyIt#add(schema, definition)
* - schema (String): rule name (fixed pattern prefix)
* - definition (String|RegExp|Object): schema definition
*
* Add new rule definition. See constructor description for details.
**/
LinkifyIt.prototype.add = function add (schema, definition) {
this.__schemas__[schema] = definition
compile(this)
return this
}
/** chainable
* LinkifyIt#set(options)
* - options (Object): { fuzzyLink|fuzzyEmail|fuzzyIP: true|false }
*
* Set recognition options for links without schema.
**/
LinkifyIt.prototype.set = function set (options) {
this.__opts__ = assign(this.__opts__, options)
return this
}
/**
* LinkifyIt#test(text) -> Boolean
*
* Searches linkifiable pattern and returns `true` on success or `false` on fail.
**/
LinkifyIt.prototype.test = function test (text) {
if (!text.length) { return false }
let m, re
// try to scan for link with schema - that's the most simple rule
if (this.re.schema_test.test(text)) {
re = this.re.schema_search
re.lastIndex = 0
while ((m = re.exec(text)) !== null) {
if (this.testSchemaAt(text, m[2], re.lastIndex)) { return true }
}
}
if (this.__opts__.fuzzyLink && this.__compiled__['http:']) {
// guess schemaless links
if (text.search(this.re.host_fuzzy_test) >= 0) {
if (text.match(this.__opts__.fuzzyIP ? this.re.link_fuzzy : this.re.link_no_ip_fuzzy) !== null) {
return true
}
}
}
if (this.__opts__.fuzzyEmail && this.__compiled__['mailto:']) {
// guess schemaless emails
if (text.indexOf('@') >= 0) {
// We can't skip this check, because this cases are possible:
// 192.168.1.1@gmail.com, my.in@example.com
if (text.match(this.re.email_fuzzy) !== null) { return true }
}
}
return false
}
/**
* LinkifyIt#pretest(text) -> Boolean
*
* Very quick check, that can give false positives. Returns true if link MAY BE
* can exists. Can be used for speed optimization, when you need to check that
* link NOT exists.
**/
LinkifyIt.prototype.pretest = function pretest (text) {
return this.re.pretest.test(text)
}
/**
* LinkifyIt#testSchemaAt(text, name, position) -> Number
* - text (String): text to scan
* - name (String): rule (schema) name
* - position (Number): text offset to check from
*
* Similar to [[LinkifyIt#test]] but checks only specific protocol tail exactly
* at given position. Returns length of found pattern (0 on fail).
**/
LinkifyIt.prototype.testSchemaAt = function testSchemaAt (text, schema, pos) {
// If not supported schema check requested - terminate
if (!this.__compiled__[schema.toLowerCase()]) {
return 0
}
return this.__compiled__[schema.toLowerCase()].validate(text, pos, this)
}
/**
* LinkifyIt#match(text) -> Array|null
*
* Returns array of found link descriptions or `null` on fail. We strongly
* recommend to use [[LinkifyIt#test]] first, for best speed.
*
* ##### Result match description
*
* - __schema__ - link schema, can be empty for fuzzy links, or `//` for
* protocol-neutral links.
* - __index__ - offset of matched text
* - __lastIndex__ - index of next char after mathch end
* - __raw__ - matched text
* - __text__ - normalized text
* - __url__ - link, generated from matched text
**/
LinkifyIt.prototype.match = function match (text) {
const result = []
const type_schemed = []
const type_fuzzy_link = []
const type_fuzzy_email = []
let m, len, re
function choose (a, b) {
if (!a) { return b }
if (!b) { return a }
if (a.index !== b.index) { return a.index < b.index ? a : b }
return a.lastIndex >= b.lastIndex ? a : b
}
if (!text.length) { return null }
// scan for links with schema
if (this.re.schema_test.test(text)) {
re = this.re.schema_search
re.lastIndex = 0
while ((m = re.exec(text)) !== null) {
len = this.testSchemaAt(text, m[2], re.lastIndex)
if (len) {
type_schemed.push({
schema: m[2],
index: m.index + m[1].length,
lastIndex: m.index + m[0].length + len
})
}
}
}
if (this.__opts__.fuzzyLink && this.__compiled__['http:']) {
re = this.__opts__.fuzzyIP ? this.re.link_fuzzy_global : this.re.link_no_ip_fuzzy_global
re.lastIndex = 0
while ((m = re.exec(text)) !== null) {
type_fuzzy_link.push({
schema: '',
index: m.index + m[1].length,
lastIndex: m.index + m[0].length
})
}
}
if (this.__opts__.fuzzyEmail && this.__compiled__['mailto:']) {
re = this.re.email_fuzzy_global
re.lastIndex = 0
while ((m = re.exec(text)) !== null) {
type_fuzzy_email.push({
schema: 'mailto:',
index: m.index + m[1].length,
lastIndex: m.index + m[0].length
})
}
}
const indexes = [0, 0, 0]
let lastIndex = 0
for (;;) {
const candidates = [
type_schemed[indexes[0]],
type_fuzzy_email[indexes[1]],
type_fuzzy_link[indexes[2]]
]
const candidate = choose(choose(candidates[0], candidates[1]), candidates[2])
if (!candidate) { break }
if (candidate === candidates[0]) {
indexes[0]++
} else if (candidate === candidates[1]) {
indexes[1]++
} else {
indexes[2]++
}
if (candidate.index < lastIndex) { continue }
const match = new Match(text, candidate.schema, candidate.index, candidate.lastIndex)
this.__compiled__[match.schema].normalize(match, this)
result.push(match)
lastIndex = candidate.lastIndex
}
if (result.length) {
return result
}
return null
}
/**
* LinkifyIt#matchAtStart(text) -> Match|null
*
* Returns fully-formed (not fuzzy) link if it starts at the beginning
* of the string, and null otherwise.
**/
LinkifyIt.prototype.matchAtStart = function matchAtStart (text) {
if (!text.length) return null
const m = this.re.schema_at_start.exec(text)
if (!m) return null
const len = this.testSchemaAt(text, m[2], m[0].length)
if (!len) return null
const match = new Match(text, m[2], m.index + m[1].length, m.index + m[0].length + len)
this.__compiled__[match.schema].normalize(match, this)
return match
}
/** chainable
* LinkifyIt#tlds(list [, keepOld]) -> this
* - list (Array): list of tlds
* - keepOld (Boolean): merge with current list if `true` (`false` by default)
*
* Load (or merge) new tlds list. Those are user for fuzzy links (without prefix)
* to avoid false positives. By default this algorythm used:
*
* - hostname with any 2-letter root zones are ok.
* - biz|com|edu|gov|net|org|pro|web|xxx|aero|asia|coop|info|museum|name|shop|рф
* are ok.
* - encoded (`xn--...`) root zones are ok.
*
* If list is replaced, then exact match for 2-chars root zones will be checked.
**/
LinkifyIt.prototype.tlds = function tlds (list, keepOld) {
list = Array.isArray(list) ? list : [list]
if (!keepOld) {
this.__tlds__ = list.slice()
this.__tlds_replaced__ = true
compile(this)
return this
}
this.__tlds__ = this.__tlds__.concat(list)
.sort()
.filter(function (el, idx, arr) {
return el !== arr[idx - 1]
})
.reverse()
compile(this)
return this
}
/**
* LinkifyIt#normalize(match)
*
* Default normalizer (if schema does not define it's own).
**/
LinkifyIt.prototype.normalize = function normalize (match) {
// Do minimal possible changes by default. Need to collect feedback prior
// to move forward https://github.com/markdown-it/linkify-it/issues/1
if (!match.schema) { match.url = `http://${match.url}` }
if (match.schema === 'mailto:' && !/^mailto:/i.test(match.url)) {
match.url = `mailto:${match.url}`
}
}
/**
* LinkifyIt#onCompile()
*
* Override to modify basic RegExp-s.
**/
LinkifyIt.prototype.onCompile = function onCompile () {
}
export default LinkifyIt
import { Any, Cc, Z, P } from 'uc.micro'
export default function (opts) {
const re = {}
opts = opts || {}
re.src_Any = Any.source
re.src_Cc = Cc.source
re.src_Z = Z.source
re.src_P = P.source
// \p{\Z\P\Cc\CF} (white spaces + control + format + punctuation)
re.src_ZPCc = [re.src_Z, re.src_P, re.src_Cc].join('|')
// \p{\Z\Cc} (white spaces + control)
re.src_ZCc = [re.src_Z, re.src_Cc].join('|')
// Experimental. List of chars, completely prohibited in links
// because can separate it from other part of text
const text_separators = '[><\uff5c]'
// All possible word characters (everything without punctuation, spaces & controls)
// Defined via punctuation & spaces to save space
// Should be something like \p{\L\N\S\M} (\w but without `_`)
re.src_pseudo_letter = `(?:(?!${text_separators}|${re.src_ZPCc})${re.src_Any})`
// The same as abothe but without [0-9]
// var src_pseudo_letter_non_d = '(?:(?![0-9]|' + src_ZPCc + ')' + src_Any + ')';
re.src_ip4 =
'(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)'
// Prohibit any of "@/[]()" in user/pass to avoid wrong domain fetch.
// Length is capped to exclude possible rescans till the end and avoid O(n^2)
// DoS. No standard limit, just take something reasonable.
re.src_auth = `(?:(?:(?!${re.src_ZCc}|[@/\\[\\]()]).){1,50}@)?`
re.src_port =
'(?::(?:6(?:[0-4]\\d{3}|5(?:[0-4]\\d{2}|5(?:[0-2]\\d|3[0-5])))|[1-5]?\\d{1,4}))?'
re.src_host_terminator =
`(?=$|${text_separators}|${re.src_ZPCc})` +
`(?!${opts['---'] ? '-(?!--)|' : '-|'}_|:\\d|\\.-|\\.(?!$|${re.src_ZPCc}))`
re.src_path =
'(?:' +
'[/?#]' +
'(?:' +
`(?!${re.src_ZCc}|${text_separators}|[()[\\]{}.,"'?!\\-;]).|` +
`\\[(?:(?!${re.src_ZCc}|\\]).)*\\]|` +
`\\((?:(?!${re.src_ZCc}|[)]).)*\\)|` +
`\\{(?:(?!${re.src_ZCc}|[}]).)*\\}|` +
`\\"(?:(?!${re.src_ZCc}|["]).)+\\"|` +
`\\'(?:(?!${re.src_ZCc}|[']).)+\\'|` +
// allow `I'm_king` if no pair found
`\\'(?=${re.src_pseudo_letter}|[-])|` +
// google has many dots in "google search" links (#66, #81).
// github has ... in commit range links,
// Restrict to
// - english
// - percent-encoded
// - parts of file path
// - params separator
// until more examples found.
'\\.{2,}[a-zA-Z0-9%/&]|' +
`\\.(?!${re.src_ZCc}|[.]|$)|` +
(opts['---']
? '\\-(?!--(?:[^-]|$))(?:-*)|' // `---` => long dash, terminate
: '\\-+|'
) +
// allow `,,,` in paths
`,(?!${re.src_ZCc}|$)|` +
// allow `;` if not followed by space-like char
`;(?!${re.src_ZCc}|$)|` +
// allow `!!!` in paths, but not at the end
`\\!+(?!${re.src_ZCc}|[!]|$)|` +
`\\?(?!${re.src_ZCc}|[?]|$)` +
')+' +
'|\\/' +
')?'
// Allow anything in markdown spec, forbid quote (") at the first position
// because emails enclosed in quotes are far more common
// Max name length capped to 64 chars (RFC 5321). This also prevents O(n^2)
// rescans to the end on inputs like `mailto:mailto:...`
re.src_email_name =
'[\\-;:&=\\+\\$,\\.a-zA-Z0-9_][\\-;:&=\\+\\$,\\"\\.a-zA-Z0-9_]{0,63}'
re.src_xn =
'xn--[a-z0-9\\-]{1,59}'
// More to read about domain names
// http://serverfault.com/questions/638260/
re.src_domain_root =
// Allow letters & digits (http://test1)
'(?:' +
re.src_xn +
'|' +
`${re.src_pseudo_letter}{1,63}` +
')'
re.src_domain =
'(?:' +
re.src_xn +
'|' +
`(?:${re.src_pseudo_letter})` +
'|' +
`(?:${re.src_pseudo_letter}(?:-|${re.src_pseudo_letter}){0,61}${re.src_pseudo_letter})` +
')'
re.src_host =
'(?:' +
// Don't need IP check, because digits are already allowed in normal domain names
// src_ip4 +
// '|' +
`(?:(?:(?:${re.src_domain})\\.)*${re.src_domain})`/* _root */ +
')'
re.tpl_host_fuzzy =
'(?:' +
re.src_ip4 +
'|' +
`(?:(?:(?:${re.src_domain})\\.)+(?:%TLDS%))` +
')'
re.tpl_host_no_ip_fuzzy =
`(?:(?:(?:${re.src_domain})\\.)+(?:%TLDS%))`
re.src_host_strict =
re.src_host + re.src_host_terminator
re.tpl_host_fuzzy_strict =
re.tpl_host_fuzzy + re.src_host_terminator
re.src_host_port_strict =
re.src_host + re.src_port + re.src_host_terminator
re.tpl_host_port_fuzzy_strict =
re.tpl_host_fuzzy + re.src_port + re.src_host_terminator
re.tpl_host_port_no_ip_fuzzy_strict =
re.tpl_host_no_ip_fuzzy + re.src_port + re.src_host_terminator
//
// Main rules
//
// Rude test fuzzy links by host, for quick deny
re.tpl_host_fuzzy_test =
`localhost|www\\.|\\.\\d{1,3}\\.|(?:\\.(?:%TLDS%)(?:${re.src_ZPCc}|>|$))`
re.tpl_email_fuzzy =
`(^|${text_separators}|"|\\(|${re.src_ZCc})` +
`(${re.src_email_name}@${re.tpl_host_fuzzy_strict})`
re.tpl_link_fuzzy =
// Fuzzy link can't be prepended with .:/\- and non punctuation.
// but can start with > (markdown blockquote)
`(^|(?![.:/\\-_@])(?:[$+<=>^\`|\uff5c]|${re.src_ZPCc}))` +
`((?![$+<=>^\`|\uff5c])${re.tpl_host_port_fuzzy_strict}${re.src_path})`
re.tpl_link_no_ip_fuzzy =
// Fuzzy link can't be prepended with .:/\- and non punctuation.
// but can start with > (markdown blockquote)
`(^|(?![.:/\\-_@])(?:[$+<=>^\`|\uff5c]|${re.src_ZPCc}))` +
`((?![$+<=>^\`|\uff5c])${re.tpl_host_port_no_ip_fuzzy_strict}${re.src_path})`
return re
}