bold()
function src/utils/markup.ts:131function bold(text: string, options?: MarkupFilterOptions): stringFormat and escape text with Discord's markdown.
import { Utils } from 'detritus-client';
Utils.Markup;Object shapes for options, payloads and events.
Standalone helpers.
function bold(text: string, options?: MarkupFilterOptions): stringfunction codeblock(text: string, options?: CodeblockFilterOptions): stringfunction codestring(text: string, options?: MarkupFilterOptions): stringfunction headerBig(text: string, options?: HeaderFilterOptions): stringfunction headerMedium(text: string, options?: HeaderFilterOptions): stringfunction headerSmall(text: string, options?: HeaderFilterOptions): stringfunction italics(text: string, options?: MarkupFilterOptions): stringfunction list(text: string, options?: ListFilterOptions): stringfunction quote(text: string, options?: QuoteFilterOptions): stringfunction spoiler(text: string, options?: MarkupFilterOptions): stringfunction strike(text: string, options?: MarkupFilterOptions): stringfunction subtext(text: string, options?: SubtextFilterOptions): stringfunction timestamp(
timestamp: string | number | Date | null,
format?: MarkupTimestampStyles,
): stringfunction trueSlice(text: string, limit?: number, escapes?: boolean): stringfunction underline(text: string, options?: MarkupFilterOptions): stringfunction url(text: string, url: string, comment?: string): stringExported values.
const escape: Readonly<{ all: (text: string, options?: MarkupFilterOptions) => string; bold: (text: string, options?: MarkupFilterOptions) => string; codeblock: (text: string, options?: CodeblockFilterOptions) => string; codestring: (text: string, options?: MarkupFilterOptions) => string; header: (text: string, options?: HeaderFilterOptions) => string; italics: (text: string, options?: MarkupFilterOptions) => string; links: (text: string, replacement?: string) => string; list: (text: string, options?: ListFilterOptions) => string; mentions: (text: string, replacement?: string) => string; quote: (text: string, options?: QuoteFilterOptions) => string; spoiler: (text: string, options?: MarkupFilterOptions) => string; strike: (text: string, options?: MarkupFilterOptions) => string; … 3 more }>export const escape = Object.freeze({
all: (text: string, options: MarkupFilterOptions = {}): string => {
const filter: MarkupFilter = Object.assign({}, defaultMarkupFilter, options);
// urls are left alone, the backslashes would end up in the link
// (the parts after a url get their line-start escapes too, which shows the same)
text = text.split(Regexes.URL_BARE).map((part, i) => {
return (i % 2) ? part : escapeAllMarkdown(part);
}).join('');
if (filter.links) {
// not `mentionEscapeCharacter`, setting that to '' would leave links working
text = escape.links(text);
}
if (filter.mentions) {
text = escape.mentions(text, filter.mentionEscapeCharacter);
}
return trueSlice(text, filter.limit);
},
bold: (text: string, options: MarkupFilterOptions = {}): string => {
const filter: MarkupFilter = Object.assign({}, defaultBoldFilter, options);
text = escapeMarker(text, Regexes[Strings.BOLD], (match: string) => {
return match.replace(Regexes[Strings.BOLD], filter.replacement);
});
if (filter.mentions) {
text = escape.mentions(text, filter.mentionEscapeCharacter);
}
return trueSlice(text, filter.limit);
},
codeblock: (text: string, options: CodeblockFilterOptions = {}): string => {
const filter: CodeblockFilter = Object.assign({}, defaultCodeblockFilter, options);
while (text.includes(Strings.CODEBLOCK)) {
text = text.replace(Regexes[Strings.CODEBLOCK], filter.replacement);
}
if (options.limit === undefined) {
filter.limit -= filter.language.length;
}
return trueSlice(text, filter.limit, false);
},
codestring: (text: string, options: MarkupFilterOptions = {}): string => {
const filter: MarkupFilter = Object.assign({}, defaultCodestringFilter, options);
text = text.replace(Regexes[Strings.CODESTRING], filter.replacement);
if (filter.mentions) {
text = escape.mentions(text, filter.mentionEscapeCharacter);
}
return trueSlice(text, filter.limit, false);
},
header: (text: string, options: HeaderFilterOptions = {}): string => {
const filter: HeaderFilter = Object.assign({}, defaultHeaderFilter, options);
text = text.replace(Regexes[Strings.HEADER], filter.replacement);
if (!filter.multiline) {
text = text.replace(/\n/g, ' ');
}
if (filter.mentions) {
text = escape.mentions(text, filter.mentionEscapeCharacter);
}
return trueSlice(text, filter.limit);
},
italics: (text: string, options: MarkupFilterOptions = {}): string => {
const filter: MarkupFilter = Object.assign({}, defaultItalicsFilter, options);
text = escapeMarker(text, Regexes[Strings.ITALICS], (match: string) => {
return match.replace(Regexes[Strings.ITALICS], filter.replacement);
});
if (filter.mentions) {
text = escape.mentions(text, filter.mentionEscapeCharacter);
}
return trueSlice(text, filter.limit);
},
links: (text: string, replacement: string = Replacements.MENTION): string => {
text = text.replace(Regexes.LINK, `]${replacement}(`);
return text;
},
list: (text: string, options: ListFilterOptions = {}): string => {
const filter: ListFilter = Object.assign({}, defaultListFilter, options);
text = text.replace(Regexes[Strings.LIST], filter.replacement);
if (!filter.multiline) {
text = text.replace(/\n/g, ' ');
}
if (filter.mentions) {
text = escape.mentions(text, filter.mentionEscapeCharacter);
}
return trueSlice(text, filter.limit);
},
mentions: (text: string, replacement: string = Replacements.MENTION): string => {
text = text.replace(Regexes.MENTION_HARDCORE, `@${replacement}`);
return text;
},
quote: (text: string, options: QuoteFilterOptions = {}): string => {
const filter: QuoteFilter = Object.assign({}, defaultQuoteFilter, options);
text = text.replace(Regexes.QUOTE_LINE_START, filter.replacement);
if (!filter.multiline) {
text = text.replace(/\n/g, ' ');
}
if (filter.mentions) {
text = escape.mentions(text, filter.mentionEscapeCharacter);
}
return trueSlice(text, filter.limit);
},
spoiler: (text: string, options: MarkupFilterOptions = {}): string => {
const filter: MarkupFilter = Object.assign({}, defaultSpoilerFilter, options);
// a lone `|` gets escaped too, `||secret|||` would leave the last one outside of the spoiler
text = escapeMarker(text, Regexes.PIPES, (match: string) => {
return (match === Strings.SPOILER) ? filter.replacement : '\\|';
});
if (filter.mentions) {
text = escape.mentions(text, filter.mentionEscapeCharacter);
}
return trueSlice(text, filter.limit);
},
strike: (text: string, options: MarkupFilterOptions = {}): string => {
const filter: MarkupFilter = Object.assign({}, defaultStrikeFilter, options);
// same as spoilers, a lone trailing `~` would end up outside of the strike
text = escapeMarker(text, Regexes.TILDES, (match: string) => {
return (match === Strings.STRIKE) ? filter.replacement : '\\~';
});
if (filter.mentions) {
text = escape.mentions(text, filter.mentionEscapeCharacter);
}
return trueSlice(text, filter.limit);
},
subtext: (text: string, options: SubtextFilterOptions = {}): string => {
const filter: SubtextFilter = Object.assign({}, defaultSubtextFilter, options);
text = text.replace(Regexes[Strings.SUBTEXT], filter.replacement);
if (!filter.multiline) {
text = text.replace(/\n/g, ' ');
}
if (filter.mentions) {
text = escape.mentions(text, filter.mentionEscapeCharacter);
}
return trueSlice(text, filter.limit);
},
underline: (text: string, options: MarkupFilterOptions = {}): string => {
const filter: MarkupFilter = Object.assign({}, defaultUnderlineFilter, options);
text = escapeMarker(text, Regexes[Strings.UNDERLINE], (match: string) => {
return match.replace(Regexes[Strings.UNDERLINE], filter.replacement);
});
if (filter.mentions) {
text = escape.mentions(text, filter.mentionEscapeCharacter);
}
return trueSlice(text, filter.limit);
},
url: (text: string, options: MarkupFilterOptions = {}): string => {
// a `<url>` wrap (no embed) is the caller's, only encode what's inside of it
const wrapped = (text.length >= 2 && text.startsWith('<') && text.endsWith('>'));
if (wrapped) {
text = text.slice(1, -1);
}
// encodes the utf-8 bytes, a char code is wrong for anything past ascii (`\u2028` isn't `%2028`)
text = text.replace(Regexes.URL, (match: string) => {
return (match === '(' || match === ')') ? '%' + match.charCodeAt(0).toString(16).toUpperCase() : encodeURIComponent(match);
});
return (wrapped) ? `<${text}>` : text;
},
});const Regexes: Readonly<{ _: RegExp; __: RegExp; -: RegExp; -#: RegExp; **: RegExp; \: RegExp; #: RegExp; `: RegExp; ```: RegExp; >: RegExp; ||: RegExp; ~~: RegExp; … 11 more }>export const Regexes = Object.freeze({
[Strings.BOLD]: /\*\*/g,
[Strings.CODEBLOCK]: new RegExp(Strings.CODEBLOCK, 'g'),
[Strings.CODESTRING]: new RegExp(Strings.CODESTRING, 'g'),
[Strings.ESCAPE]: /\\/g,
// `m` so every line's start gets escaped, not just the text's
[Strings.HEADER]: /^(\s*)#/gm,
[Strings.ITALICS]: /(_|\*)/g,
[Strings.LIST]: /(-|\*)/g,
[Strings.QUOTE]: />/g,
[Strings.SPOILER]: /\|\|/g,
[Strings.STRIKE]: new RegExp(Strings.STRIKE, 'g'),
[Strings.SUBTEXT]: /^(\s*)(-?#)/gm,
[Strings.UNDERLINE]: new RegExp(Strings.UNDERLINE, 'g'),
EVERYONE: /@(everyone|here)/g,
LINK: /\]\(/g,
LINK_TEXT: /[\[\]]/g,
// list markers at the start of a line (`- `, `* `, `1. `)
LIST_LINE_START: /^(\s*)([-*]|\d+\.)(?=\s)/gm,
MENTION: /<@([!&]?[0-9]{16,21})>/g,
MENTION_HARDCORE: /@/g,
// a lone `|`/`~` next to the closing marker would leave one outside of it
PIPES: /\|\|?/g,
// only a `>` at the start of a line is a quote, the rest are emojis/channels/timestamps
QUOTE_LINE_START: /^(\s*)>/gm,
TILDES: /~~?/g,
// anything that would end the link's url early (`\` escapes the `)`, `>` gets read as `<url>`'s end)
URL: /[()<>\s\\]/g,
// how discord autolinks, markdown inside these isn't parsed so escaping them only puts backslashes in the link
URL_BARE: /(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/g,const Replacements: Readonly<{ _: "\\$1"; __: "\\_\\_"; -: "\\$1"; -#: "$1\\$2"; **: "\\*\\*"; \: "\\\\"; #: "$1\\#"; `: "\\`"; ```: "```"; >: "\\>"; ||: "\\|\\|"; ~~: "\\~\\~"; … 3 more }>export const Replacements = Object.freeze({
[Strings.BOLD]: '\\*\\*',
[Strings.CODEBLOCK]: '``\u200b`',
[Strings.CODESTRING]: '\\`',
[Strings.ESCAPE]: '\\\\',
LINK_TEXT: '\\$&',
[Strings.HEADER]: '$1\\#',
[Strings.ITALICS]: '\\$1',
[Strings.LIST]: '\\$1',
[Strings.QUOTE]: '\\>',
QUOTE_LINE_START: '$1\\>',
[Strings.SPOILER]: '\\|\\|',
[Strings.STRIKE]: '\\~\\~',
[Strings.SUBTEXT]: '$1\\$2',
[Strings.UNDERLINE]: '\\_\\_',
MENTION: '\u200b',
});const Strings: Readonly<{ BOLD: "**"; CODEBLOCK: "```"; CODESTRING: "`"; CODESTRING_DOUBLE: "``"; ESCAPE: "\\"; HEADER: "#"; ITALICS: "_"; LIST: "-"; QUOTE: ">"; SPOILER: "||"; STRIKE: "~~"; SUBTEXT: "-#"; … 1 more }>export const Strings = Object.freeze({
BOLD: '**',
CODEBLOCK: '```',
CODESTRING: '`',
CODESTRING_DOUBLE: '``',
ESCAPE: '\\',
HEADER: '#',
ITALICS: '_',
LIST: '-',
QUOTE: '>',
SPOILER: '||',
STRIKE: '~~',
SUBTEXT: '-#',
UNDERLINE: '__',
});