UNPKG

linkifyjs

Version:

Find URLs, email addresses, #hashtags and @mentions in plain-text strings, then convert them into HTML <a> links.

639 lines (637 loc) 21.9 kB
export type Transition<T> = null | T; /** * Scanner output token: * - `t` is the token name (e.g., 'NUM', 'EMOJI', 'TLD') * - `v` is the value of the token (e.g., '123', '❤️', 'com') * - `s` is the start index of the token in the original string * - `e` is the end index of the token in the original string */ export type Token = { t: string; v: string; s: number; e: number; }; export type Collections<T> = { [collection: string]: T[]; }; export type ScannerInit = { start: State<string>; tokens: { groups: Collections<string>; } & typeof tk; }; export type ParserInit = { start: State<MultiToken>; tokens: typeof multi; }; export type TokenPlugin = (arg: { scanner: ScannerInit; }) => void; export type Plugin = (arg: { scanner: ScannerInit; parser: ParserInit; }) => void; export type Flags = { [group: string]: true; }; /** * An object where each key is a valid DOM Event Name such as `click` or `focus` * and each value is an event handler function. * * https://developer.mozilla.org/en-US/docs/Web/API/Element#events */ export type EventListeners = { [event: string]: Function; } | null; /** * All formatted properties required to render a link, including `tagName`, * `attributes`, `content` and `eventListeners`. */ export type IntermediateRepresentation = { tagName: any; attributes: { [attr: string]: any; }; content: string; eventListeners: EventListeners; }; /** * Specify either an object described by the template type `O` or a function. * * The function takes a string value (usually the link's href attribute), the * link type (`'url'`, `'hashtag`', etc.) and an internal token representation * of the link. It should return an object of the template type `O` */ export type OptObj<O> = O | ((value: string, type: string, token: MultiToken) => O); /** * Specify either a function described by template type `F` or an object. * * Each key in the object should be a link type (`'url'`, `'hashtag`', etc.). Each * value should be a function with template type `F` that is called when the * corresponding link type is encountered. */ export type OptFn<F> = F | { [type: string]: F; }; /** * Specify either a value with template type `V`, a function that returns `V` or * an object where each value resolves to `V`. * * The function takes a string value (usually the link's href attribute), the * link type (`'url'`, `'hashtag`', etc.) and an internal token representation * of the link. It should return an object of the template type `V` * * For the object, each key should be a link type (`'url'`, `'hashtag`', etc.). * Each value should either have type `V` or a function that returns V. This * function similarly takes a string value and a token. * * Example valid types for `Opt<string>`: * * ```js * 'hello' * (value, type, token) => 'world' * { url: 'hello', email: (value, token) => 'world'} * ``` */ export type Opt<V> = V | ((value: string, type: string, token: MultiToken) => V) | { [type: string]: V | ((value: string, token: MultiToken) => V); }; /** * See available options: https://linkify.js.org/docs/options.html */ export type Opts = { defaultProtocol?: string; events?: OptObj<EventListeners>; format?: Opt<string>; formatHref?: Opt<string>; nl2br?: boolean; tagName?: Opt<any>; target?: Opt<string>; rel?: Opt<string>; validate?: Opt<boolean>; truncate?: Opt<number>; className?: Opt<string>; attributes?: OptObj<({ [attr: string]: any; })>; ignoreTags?: string[]; render?: OptFn<((ir: IntermediateRepresentation) => any)>; }; /****************************************************************************** Multi-Tokens Tokens composed of arrays of TextTokens ******************************************************************************/ /** * @param {string} value * @param {Token[]} tokens */ export function MultiToken(value: string, tokens: Token[]): void; export class MultiToken { /****************************************************************************** Multi-Tokens Tokens composed of arrays of TextTokens ******************************************************************************/ /** * @param {string} value * @param {Token[]} tokens */ constructor(value: string, tokens: Token[]); t: string; v: string; tk: Token[]; isLink: boolean; /** * Return the string this token represents. * @return {string} */ toString(): string; /** * What should the value for this token be in the `href` HTML attribute? * Returns the `.toString` value by default. * @param {string} [scheme] * @return {string} */ toHref(scheme?: string): string; /** * @param {Options} options Formatting options * @returns {string} */ toFormattedString(options: Options): string; /** * * @param {Options} options * @returns {string} */ toFormattedHref(options: Options): string; /** * The start index of this token in the original input string * @returns {number} */ startIndex(): number; /** * The end index of this token in the original input string (up to this * index but not including it) * @returns {number} */ endIndex(): number; /** Returns an object of relevant values for this token, which includes keys * type - Kind of token ('url', 'email', etc.) * value - Original text * href - The value that should be added to the anchor tag's href attribute @method toObject @param {string} [protocol] `'http'` by default */ toObject(protocol?: string): { type: string; value: string; isLink: boolean; href: string; start: number; end: number; }; /** * * @param {Options} options Formatting option */ toFormattedObject(options: Options): { type: string; value: string; isLink: boolean; href: string; start: number; end: number; }; /** * Whether this token should be rendered as a link according to the given options * @param {Options} options * @returns {boolean} */ validate(options: Options): boolean; /** * Return an object that represents how this link should be rendered. * @param {Options} options Formattinng options */ render(options: Options): { tagName: any; attributes: { href: any; class: any; target: any; rel: any; }; content: string; eventListeners: any; }; } /** * Utility class for linkify interfaces to apply specified * {@link Opts formatting and rendering options}. * * @param {Opts | Options} [opts] Option value overrides. * @param {(ir: IntermediateRepresentation) => any} [defaultRender] (For * internal use) default render function that determines how to generate an * HTML element based on a link token's derived tagName, attributes and HTML. * Similar to render option */ export function Options(opts?: Opts | Options, defaultRender?: (ir: IntermediateRepresentation) => any): void; export class Options { /** * Utility class for linkify interfaces to apply specified * {@link Opts formatting and rendering options}. * * @param {Opts | Options} [opts] Option value overrides. * @param {(ir: IntermediateRepresentation) => any} [defaultRender] (For * internal use) default render function that determines how to generate an * HTML element based on a link token's derived tagName, attributes and HTML. * Similar to render option */ constructor(opts?: Opts | Options, defaultRender?: (ir: IntermediateRepresentation) => any); /** @protected */ o: Required<Opts>; defaultRender: any; ignoreTags: string[]; /** * Returns true or false based on whether a token should be displayed as a * link based on the user options. * @param {MultiToken} token * @returns {boolean} */ check(token: MultiToken): boolean; /** * Resolve an option's value based on the value of the option and the given * params. If operator and token are specified and the target option is * callable, automatically calls the function with the given argument. * @template {keyof Opts} K * @param {K} key Name of option to use * @param {string} [operator] will be passed to the target option if it's a * function. If not specified, RAW function value gets returned * @param {MultiToken} [token] The token from linkify.tokenize * @returns {Opts[K] | any} */ get<K extends keyof Opts>(key: K, operator?: string, token?: MultiToken): Opts[K] | any; /** * @template {keyof Opts} L * @param {L} key Name of options object to use * @param {string} [operator] * @param {MultiToken} [token] * @returns {Opts[L] | any} */ getObj<L extends keyof Opts>(key: L, operator?: string, token?: MultiToken): Opts[L] | any; /** * Convert the given token to a rendered element that may be added to the * calling-interface's DOM * @param {MultiToken} token Token to render to an HTML element * @returns {any} Render result; e.g., HTML string, DOM element, React * Component, etc. */ render(token: MultiToken): any; } /** * @template T * @typedef {null | T } Transition */ /** * Define a basic state machine state. j is the list of character transitions, * jr is the list of regex-match transitions, jd is the default state to * transition to t is the accepting token type, if any. If this is the terminal * state, then it does not emit a token. * * The template type T represents the type of the token this state accepts. This * should be a string (such as of the token exports in `text.js`) or a * MultiToken subclass (from `multi.js`) * * @template T * @param {T} [token] Token that this state emits */ export function State<T>(token?: T): void; export class State<T> { /** * @template T * @typedef {null | T } Transition */ /** * Define a basic state machine state. j is the list of character transitions, * jr is the list of regex-match transitions, jd is the default state to * transition to t is the accepting token type, if any. If this is the terminal * state, then it does not emit a token. * * The template type T represents the type of the token this state accepts. This * should be a string (such as of the token exports in `text.js`) or a * MultiToken subclass (from `multi.js`) * * @template T * @param {T} [token] Token that this state emits */ constructor(token?: T); /** @type {{ [input: string]: State<T> }} j */ j: { [input: string]: State<T>; }; /** @type {[RegExp, State<T>][]} jr */ jr: [RegExp, State<T>][]; /** @type {?State<T>} jd */ jd: State<T> | null; /** @type {?T} t */ t: T | null; accepts(): boolean; /** * Follow an existing transition from the given input to the next state. * Does not mutate. * @param {string} input character or token type to transition on * @returns {?State<T>} the next state, if any */ go(input: string): State<T> | null; /** * Whether the state has a transition for the given input. Set the second * argument to true to only look for an exact match (and not a default or * regular-expression-based transition) * @param {string} input * @param {boolean} exactOnly */ has(input: string, exactOnly?: boolean): boolean; /** * Short for "transition all"; create a transition from the array of items * in the given list to the same final resulting state. * @param {string | string[]} inputs Group of inputs to transition on * @param {Transition<T> | State<T>} [next] Transition options * @param {Flags} [flags] Collections flags to add token to * @param {Collections<T>} [groups] Master list of token groups */ ta(inputs: string | string[], next?: Transition<T> | State<T>, flags?: Flags, groups?: Collections<T>): void; /** * Short for "take regexp transition"; defines a transition for this state * when it encounters a token which matches the given regular expression * @param {RegExp} regexp Regular expression transition (populate first) * @param {T | State<T>} [next] Transition options * @param {Flags} [flags] Collections flags to add token to * @param {Collections<T>} [groups] Master list of token groups * @returns {State<T>} taken after the given input */ tr(regexp: RegExp, next?: T | State<T>, flags?: Flags, groups?: Collections<T>): State<T>; /** * Short for "take transitions", will take as many sequential transitions as * the length of the given input and returns the * resulting final state. * @param {string | string[]} input * @param {T | State<T>} [next] Transition options * @param {Flags} [flags] Collections flags to add token to * @param {Collections<T>} [groups] Master list of token groups * @returns {State<T>} taken after the given input */ ts(input: string | string[], next?: T | State<T>, flags?: Flags, groups?: Collections<T>): State<T>; /** * Short for "take transition", this is a method for building/working with * state machines. * * If a state already exists for the given input, returns it. * * If a token is specified, that state will emit that token when reached by * the linkify engine. * * If no state exists, it will be initialized with some default transitions * that resemble existing default transitions. * * If a state is given for the second argument, that state will be * transitioned to on the given input regardless of what that input * previously did. * * Specify a token group flags to define groups that this token belongs to. * The token will be added to corresponding entires in the given groups * object. * * @param {string} input character, token type to transition on * @param {T | State<T>} [next] Transition options * @param {Flags} [flags] Collections flags to add token to * @param {Collections<T>} [groups] Master list of groups * @returns {State<T>} taken after the given input */ tt(input: string, next?: T | State<T>, flags?: Flags, groups?: Collections<T>): State<T>; } export namespace State { export { groups }; } /** * Create a new token that can be emitted by the parser state machine * @param {string} type readable type of the token * @param {object} props properties to assign or override, including isLink = true or false * @returns {new (value: string, tokens: Token[]) => MultiToken} new token class */ export function createTokenClass(type: string, props: object): new (value: string, tokens: Token[]) => MultiToken; /** * Find a list of linkable items in the given string. * @param {string} str string to find links in * @param {string | Opts} [type] either formatting options or specific type of * links to find, e.g., 'url' or 'email' * @param {Opts} [opts] formatting options for final output. Cannot be specified * if opts already provided in `type` argument */ export function find(str: string, type?: string | Opts, opts?: Opts): { type: string; value: string; isLink: boolean; href: string; start: number; end: number; }[]; /** * Initialize the linkify state machine. Called automatically the first time * linkify is called on a string, but may be called manually as well. */ export function init(): { scanner: any; parser: any; tokenQueue: any[]; pluginQueue: any[]; customSchemes: any[]; initialized: boolean; }; export var multi: Readonly<{ __proto__: any; MultiToken: typeof MultiToken; Base: typeof MultiToken; createTokenClass: typeof createTokenClass; Email: new (value: string, tokens: Token[]) => MultiToken; Text: new (value: string, tokens: Token[]) => MultiToken; Nl: new (value: string, tokens: Token[]) => MultiToken; Url: new (value: string, tokens: Token[]) => MultiToken; }>; export var options: Readonly<{ __proto__: any; defaults: Required<Opts>; Options: typeof Options; assign: <A, B>(target: A, properties: B) => A & B; }>; export var regexp: Readonly<{ __proto__: any; ASCII_LETTER: RegExp; LETTER: RegExp; EMOJI: RegExp; EMOJI_VARIATION: RegExp; DIGIT: RegExp; SPACE: RegExp; }>; /** * Detect URLs with the following additional protocol. Anything with format * "protocol://..." will be considered a link. If `optionalSlashSlash` is set to * `true`, anything with format "protocol:..." will be considered a link. * @param {string} scheme * @param {boolean} [optionalSlashSlash] */ export function registerCustomProtocol(scheme: string, optionalSlashSlash?: boolean): void; /** * Register a linkify plugin * @param {string} name of plugin to register * @param {Plugin} plugin function that accepts the parser state machine and * extends the parser to recognize additional link types */ export function registerPlugin(name: string, plugin: Plugin): void; /** * Register a token plugin to allow the scanner to recognize additional token * types before the parser state machine is constructed from the results. * @param {string} name of plugin to register * @param {TokenPlugin} plugin function that accepts the scanner state machine * and available scanner tokens and collections and extends the state machine to * recognize additional tokens or groups. */ export function registerTokenPlugin(name: string, plugin: TokenPlugin): void; /** * @typedef {{ * start: State<string>, * tokens: { groups: Collections<string> } & typeof tk * }} ScannerInit */ /** * @typedef {{ * start: State<MultiToken>, * tokens: typeof multi * }} ParserInit */ /** * @typedef {(arg: { scanner: ScannerInit }) => void} TokenPlugin */ /** * @typedef {(arg: { scanner: ScannerInit, parser: ParserInit }) => void} Plugin */ /** * De-register all plugins and reset the internal state-machine. Used for * testing; not required in practice. * @private */ export function reset(): { scanner: any; parser: any; tokenQueue: any[]; pluginQueue: any[]; customSchemes: any[]; initialized: boolean; }; /** * Convert a String to an Array of characters, taking into account that some * characters like emojis take up two string indexes. * * Adapted from core-js (MIT license) * https://github.com/zloirock/core-js/blob/2d69cf5f99ab3ea3463c395df81e5a15b68f49d9/packages/core-js/internals/string-multibyte.js * * @function stringToArray * @param {string} str * @returns {string[]} */ export function stringToArray(str: string): string[]; /** * Is the given string valid linkable text of some sort. Note that this does not * trim the text for you. * * Optionally pass in a second `type` param, which is the type of link to test * for. * * For example, * * linkify.test(str, 'email'); * * Returns `true` if str is a valid email. * @param {string} str string to test for links * @param {string} [type] optional specific link type to look for * @returns boolean true/false */ export function test(str: string, type?: string): boolean; /** * Parse a string into tokens that represent linkable and non-linkable sub-components * @param {string} str * @return {MultiToken[]} tokens */ export function tokenize(str: string): MultiToken[]; export namespace tokenize { export { run$1 as scan }; } declare var tk: Readonly<{ __proto__: any; WORD: "WORD"; UWORD: "UWORD"; ASCIINUMERICAL: "ASCIINUMERICAL"; ALPHANUMERICAL: "ALPHANUMERICAL"; LOCALHOST: "LOCALHOST"; TLD: "TLD"; UTLD: "UTLD"; SCHEME: "SCHEME"; SLASH_SCHEME: "SLASH_SCHEME"; NUM: "NUM"; WS: "WS"; NL: "NL"; OPENBRACE: "OPENBRACE"; CLOSEBRACE: "CLOSEBRACE"; OPENBRACKET: "OPENBRACKET"; CLOSEBRACKET: "CLOSEBRACKET"; OPENPAREN: "OPENPAREN"; CLOSEPAREN: "CLOSEPAREN"; OPENANGLEBRACKET: "OPENANGLEBRACKET"; CLOSEANGLEBRACKET: "CLOSEANGLEBRACKET"; FULLWIDTHLEFTPAREN: "FULLWIDTHLEFTPAREN"; FULLWIDTHRIGHTPAREN: "FULLWIDTHRIGHTPAREN"; LEFTCORNERBRACKET: "LEFTCORNERBRACKET"; RIGHTCORNERBRACKET: "RIGHTCORNERBRACKET"; LEFTWHITECORNERBRACKET: "LEFTWHITECORNERBRACKET"; RIGHTWHITECORNERBRACKET: "RIGHTWHITECORNERBRACKET"; FULLWIDTHLESSTHAN: "FULLWIDTHLESSTHAN"; FULLWIDTHGREATERTHAN: "FULLWIDTHGREATERTHAN"; AMPERSAND: "AMPERSAND"; APOSTROPHE: "APOSTROPHE"; ASTERISK: "ASTERISK"; AT: "AT"; BACKSLASH: "BACKSLASH"; BACKTICK: "BACKTICK"; CARET: "CARET"; COLON: "COLON"; COMMA: "COMMA"; DOLLAR: "DOLLAR"; DOT: "DOT"; EQUALS: "EQUALS"; EXCLAMATION: "EXCLAMATION"; HYPHEN: "HYPHEN"; PERCENT: "PERCENT"; PIPE: "PIPE"; PLUS: "PLUS"; POUND: "POUND"; QUERY: "QUERY"; QUOTE: "QUOTE"; FULLWIDTHMIDDLEDOT: "FULLWIDTHMIDDLEDOT"; SEMI: "SEMI"; SLASH: "SLASH"; TILDE: "TILDE"; UNDERSCORE: "UNDERSCORE"; EMOJI: "EMOJI"; SYM: "SYM"; }>; declare var groups: Collections<string>; /** Given a string, returns an array of TOKEN instances representing the composition of that string. @method run @param {State<string>} start scanner starting state @param {string} str input string to scan @return {Token[]} list of tokens, each with a type and value */ declare function run$1(start: State<string>, str: string): Token[]; export { multi as text };