mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-17 20:13:30 +01:00
feat(Util): backport escapeMarkdown PRs to v13 (#8703)
This commit is contained in:
@@ -115,15 +115,20 @@ class Util extends null {
|
|||||||
/**
|
/**
|
||||||
* Options used to escape markdown.
|
* Options used to escape markdown.
|
||||||
* @typedef {Object} EscapeMarkdownOptions
|
* @typedef {Object} EscapeMarkdownOptions
|
||||||
* @property {boolean} [codeBlock=true] Whether to escape code blocks or not
|
* @property {boolean} [codeBlock=true] Whether to escape code blocks
|
||||||
* @property {boolean} [inlineCode=true] Whether to escape inline code or not
|
* @property {boolean} [inlineCode=true] Whether to escape inline code
|
||||||
* @property {boolean} [bold=true] Whether to escape bolds or not
|
* @property {boolean} [bold=true] Whether to escape bolds
|
||||||
* @property {boolean} [italic=true] Whether to escape italics or not
|
* @property {boolean} [italic=true] Whether to escape italics
|
||||||
* @property {boolean} [underline=true] Whether to escape underlines or not
|
* @property {boolean} [underline=true] Whether to escape underlines
|
||||||
* @property {boolean} [strikethrough=true] Whether to escape strikethroughs or not
|
* @property {boolean} [strikethrough=true] Whether to escape strikethroughs
|
||||||
* @property {boolean} [spoiler=true] Whether to escape spoilers or not
|
* @property {boolean} [spoiler=true] Whether to escape spoilers
|
||||||
* @property {boolean} [codeBlockContent=true] Whether to escape text inside code blocks or not
|
* @property {boolean} [codeBlockContent=true] Whether to escape text inside code blocks
|
||||||
* @property {boolean} [inlineCodeContent=true] Whether to escape text inside inline code or not
|
* @property {boolean} [inlineCodeContent=true] Whether to escape text inside inline code
|
||||||
|
* @property {boolean} [escape=true] Whether to escape escape characters
|
||||||
|
* @property {boolean} [heading=false] Whether to escape headings
|
||||||
|
* @property {boolean} [bulletedList=false] Whether to escape bulleted lists
|
||||||
|
* @property {boolean} [numberedList=false] Whether to escape numbered lists
|
||||||
|
* @property {boolean} [maskedLink=false] Whether to escape masked links
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -144,6 +149,11 @@ class Util extends null {
|
|||||||
spoiler = true,
|
spoiler = true,
|
||||||
codeBlockContent = true,
|
codeBlockContent = true,
|
||||||
inlineCodeContent = true,
|
inlineCodeContent = true,
|
||||||
|
escape = true,
|
||||||
|
heading = false,
|
||||||
|
bulletedList = false,
|
||||||
|
numberedList = false,
|
||||||
|
maskedLink = false,
|
||||||
} = {},
|
} = {},
|
||||||
) {
|
) {
|
||||||
if (!codeBlockContent) {
|
if (!codeBlockContent) {
|
||||||
@@ -159,6 +169,11 @@ class Util extends null {
|
|||||||
strikethrough,
|
strikethrough,
|
||||||
spoiler,
|
spoiler,
|
||||||
inlineCodeContent,
|
inlineCodeContent,
|
||||||
|
escape,
|
||||||
|
heading,
|
||||||
|
bulletedList,
|
||||||
|
numberedList,
|
||||||
|
maskedLink,
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.join(codeBlock ? '\\`\\`\\`' : '```');
|
.join(codeBlock ? '\\`\\`\\`' : '```');
|
||||||
@@ -175,6 +190,11 @@ class Util extends null {
|
|||||||
underline,
|
underline,
|
||||||
strikethrough,
|
strikethrough,
|
||||||
spoiler,
|
spoiler,
|
||||||
|
escape,
|
||||||
|
heading,
|
||||||
|
bulletedList,
|
||||||
|
numberedList,
|
||||||
|
maskedLink,
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.join(inlineCode ? '\\`' : '`');
|
.join(inlineCode ? '\\`' : '`');
|
||||||
@@ -186,6 +206,11 @@ class Util extends null {
|
|||||||
if (underline) text = Util.escapeUnderline(text);
|
if (underline) text = Util.escapeUnderline(text);
|
||||||
if (strikethrough) text = Util.escapeStrikethrough(text);
|
if (strikethrough) text = Util.escapeStrikethrough(text);
|
||||||
if (spoiler) text = Util.escapeSpoiler(text);
|
if (spoiler) text = Util.escapeSpoiler(text);
|
||||||
|
if (escape) text = Util.escapeEscape(text);
|
||||||
|
if (heading) text = Util.escapeHeading(text);
|
||||||
|
if (bulletedList) text = Util.escapeBulletedList(text);
|
||||||
|
if (numberedList) text = Util.escapeNumberedList(text);
|
||||||
|
if (maskedLink) text = Util.escapeMaskedLink(text);
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,7 +229,7 @@ class Util extends null {
|
|||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
static escapeInlineCode(text) {
|
static escapeInlineCode(text) {
|
||||||
return text.replace(/(?<=^|[^`])`(?=[^`]|$)/g, '\\`');
|
return text.replace(/(?<=^|[^`])``?(?=[^`]|$)/g, match => (match.length === 2 ? '\\`\\`' : '\\`'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -269,6 +294,51 @@ class Util extends null {
|
|||||||
return text.replaceAll('||', '\\|\\|');
|
return text.replaceAll('||', '\\|\\|');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escapes escape characters in a string.
|
||||||
|
* @param {string} text Content to escape
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
static escapeEscape(text) {
|
||||||
|
return text.replaceAll('\\', '\\\\');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escapes heading characters in a string.
|
||||||
|
* @param {string} text Content to escape
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
static escapeHeading(text) {
|
||||||
|
return text.replaceAll(/^( {0,2}[*-] +)?(#{1,3} )/gm, '$1\\$2');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escapes bulleted list characters in a string.
|
||||||
|
* @param {string} text Content to escape
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
static escapeBulletedList(text) {
|
||||||
|
return text.replaceAll(/^( *)[*-]( +)/gm, '$1\\-$2');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escapes numbered list characters in a string.
|
||||||
|
* @param {string} text Content to escape
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
static escapeNumberedList(text) {
|
||||||
|
return text.replaceAll(/^( *\d+)\./gm, '$1\\.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escapes masked link characters in a string.
|
||||||
|
* @param {string} text Content to escape
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
static escapeMaskedLink(text) {
|
||||||
|
return text.replaceAll(/\[.+\]\(.+\)/gm, '\\$&');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} FetchRecommendedShardsOptions
|
* @typedef {Object} FetchRecommendedShardsOptions
|
||||||
* @property {number} [guildsPerShard=1000] Number of guilds assigned per shard
|
* @property {number} [guildsPerShard=1000] Number of guilds assigned per shard
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
const Util = require('../src/util/Util');
|
const Util = require('../src/util/Util');
|
||||||
const testString = "`_Behold!_`\n||___~~***```js\n`use strict`;\nrequire('discord.js');```***~~___||";
|
const testString = "`_Behold!_`\n||___~~***```js\n`use strict`;\nrequire('discord.js');```***~~___||";
|
||||||
|
const testStringForums =
|
||||||
|
'# Title\n## Subtitle\n### Subsubtitle\n- Bullet list\n - # Title with bullet\n * Subbullet\n1. Number list\n 1. Sub number list';
|
||||||
|
|
||||||
describe('escapeCodeblock', () => {
|
describe('escapeCodeblock', () => {
|
||||||
test('shared', () => {
|
test('shared', () => {
|
||||||
@@ -94,6 +96,48 @@ describe('escapeSpoiler', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('escapeHeading', () => {
|
||||||
|
test('shared', () => {
|
||||||
|
expect(Util.escapeHeading(testStringForums)).toBe(
|
||||||
|
'\\# Title\n\\## Subtitle\n\\### Subsubtitle\n- Bullet list\n - \\# Title with bullet\n * Subbullet\n1. Number list\n 1. Sub number list',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('basic', () => {
|
||||||
|
expect(Util.escapeHeading('# test')).toBe('\\# test');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('escapeBulletedList', () => {
|
||||||
|
test('shared', () => {
|
||||||
|
expect(Util.escapeBulletedList(testStringForums)).toBe(
|
||||||
|
'# Title\n## Subtitle\n### Subsubtitle\n\\- Bullet list\n \\- # Title with bullet\n \\* Subbullet\n1. Number list\n 1. Sub number list',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('basic', () => {
|
||||||
|
expect(Util.escapeBulletedList('- test')).toBe('\\- test');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('escapeNumberedList', () => {
|
||||||
|
test('shared', () => {
|
||||||
|
expect(Util.escapeNumberedList(testStringForums)).toBe(
|
||||||
|
'# Title\n## Subtitle\n### Subsubtitle\n- Bullet list\n - # Title with bullet\n * Subbullet\n1\\. Number list\n 1\\. Sub number list',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('basic', () => {
|
||||||
|
expect(Util.escapeNumberedList('1. test')).toBe('1\\. test');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('escapeMaskedLink', () => {
|
||||||
|
test('basic', () => {
|
||||||
|
expect(Util.escapeMaskedLink('[test](https://discord.js.org)')).toBe('\\[test](https://discord.js.org)');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('escapeMarkdown', () => {
|
describe('escapeMarkdown', () => {
|
||||||
test('shared', () => {
|
test('shared', () => {
|
||||||
expect(Util.escapeMarkdown(testString)).toBe(
|
expect(Util.escapeMarkdown(testString)).toBe(
|
||||||
@@ -176,7 +220,7 @@ describe('escapeMarkdown', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('edge-case odd number of fenses with no code block content', () => {
|
test('edge-case odd number of fences with no code block content', () => {
|
||||||
expect(
|
expect(
|
||||||
Util.escapeMarkdown('**foo** ```**bar**``` **fizz** ``` **buzz**', {
|
Util.escapeMarkdown('**foo** ```**bar**``` **fizz** ``` **buzz**', {
|
||||||
codeBlock: false,
|
codeBlock: false,
|
||||||
|
|||||||
12
typings/index.d.ts
vendored
12
typings/index.d.ts
vendored
@@ -2625,6 +2625,11 @@ export class Util extends null {
|
|||||||
public static escapeUnderline(text: string): string;
|
public static escapeUnderline(text: string): string;
|
||||||
public static escapeStrikethrough(text: string): string;
|
public static escapeStrikethrough(text: string): string;
|
||||||
public static escapeSpoiler(text: string): string;
|
public static escapeSpoiler(text: string): string;
|
||||||
|
public static escapeEscape(text: string): string;
|
||||||
|
public static escapeHeading(text: string): string;
|
||||||
|
public static escapeBulletedList(text: string): string;
|
||||||
|
public static escapeNumberedList(text: string): string;
|
||||||
|
public static escapeMaskedLink(text: string): string;
|
||||||
public static cleanCodeBlockContent(text: string): string;
|
public static cleanCodeBlockContent(text: string): string;
|
||||||
public static fetchRecommendedShards(token: string, options?: FetchRecommendedShardsOptions): Promise<number>;
|
public static fetchRecommendedShards(token: string, options?: FetchRecommendedShardsOptions): Promise<number>;
|
||||||
public static flatten(obj: unknown, ...props: Record<string, boolean | string>[]): unknown;
|
public static flatten(obj: unknown, ...props: Record<string, boolean | string>[]): unknown;
|
||||||
@@ -4650,8 +4655,13 @@ export interface EscapeMarkdownOptions {
|
|||||||
underline?: boolean;
|
underline?: boolean;
|
||||||
strikethrough?: boolean;
|
strikethrough?: boolean;
|
||||||
spoiler?: boolean;
|
spoiler?: boolean;
|
||||||
inlineCodeContent?: boolean;
|
|
||||||
codeBlockContent?: boolean;
|
codeBlockContent?: boolean;
|
||||||
|
inlineCodeContent?: boolean;
|
||||||
|
escape?: boolean;
|
||||||
|
heading?: boolean;
|
||||||
|
bulletedList?: boolean;
|
||||||
|
numberedList?: boolean;
|
||||||
|
maskedLink?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ExplicitContentFilterLevel = keyof typeof ExplicitContentFilterLevels;
|
export type ExplicitContentFilterLevel = keyof typeof ExplicitContentFilterLevels;
|
||||||
|
|||||||
Reference in New Issue
Block a user