PERF: Avoid excessive object creations in watched words (#27354)

Inline the helper functions, avoid creating and then immediately destructuring arrays, use complete strings instead of string interpolation, Map instead of a pojo.
This commit is contained in:
Jarek Radosz 2024-06-10 14:44:31 +02:00 committed by GitHub
parent 71391cd40d
commit 42a529f9ae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 51 additions and 67 deletions

View File

@ -1,9 +1,5 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import {
createWatchedWordRegExp,
toWatchedWord,
} from "discourse-common/utils/watched-words";
export default class WatchedWordTest extends Component {
@tracked value;
@ -31,7 +27,10 @@ export default class WatchedWordTest extends Component {
if (this.isReplace || this.isLink) {
const matches = [];
this.args.model.watchedWord.words.forEach((word) => {
const regexp = createWatchedWordRegExp(word);
const regexp = new RegExp(
word.regexp,
word.case_sensitive ? "gu" : "gui"
);
let match;
while ((match = regexp.exec(this.value)) !== null) {
@ -42,38 +41,44 @@ export default class WatchedWordTest extends Component {
}
});
return matches;
} else if (this.isTag) {
const matches = {};
}
if (this.isTag) {
const matches = new Map();
this.args.model.watchedWord.words.forEach((word) => {
const regexp = createWatchedWordRegExp(word);
const regexp = new RegExp(
word.regexp,
word.case_sensitive ? "gu" : "gui"
);
let match;
while ((match = regexp.exec(this.value)) !== null) {
if (!matches[match[1]]) {
matches[match[1]] = new Set();
if (!matches.has(match[1])) {
matches.set(match[1], new Set());
}
let tags = matches[match[1]];
word.replacement.split(",").forEach((tag) => {
tags.add(tag);
});
const tags = matches.get(match[1]);
word.replacement.split(",").forEach((tag) => tags.add(tag));
}
});
return Object.entries(matches).map((entry) => ({
match: entry[0],
tags: Array.from(entry[1]),
return Array.from(matches, ([match, tagsSet]) => ({
match,
tags: Array.from(tagsSet),
}));
} else {
let matches = [];
this.args.model.watchedWord.compiledRegularExpression.forEach(
(regexp) => {
const wordRegexp = createWatchedWordRegExp(toWatchedWord(regexp));
matches.push(...(this.value.match(wordRegexp) || []));
}
}
let matches = [];
this.args.model.watchedWord.compiledRegularExpression.forEach((entry) => {
const [regexp, options] = Object.entries(entry)[0];
const wordRegexp = new RegExp(
regexp,
options.case_sensitive ? "gu" : "gui"
);
return matches;
}
matches.push(...(this.value.match(wordRegexp) || []));
});
return matches;
}
}

View File

@ -1,9 +0,0 @@
export function createWatchedWordRegExp(word) {
const caseFlag = word.case_sensitive ? "" : "i";
return new RegExp(word.regexp, `${caseFlag}gu`);
}
export function toWatchedWord(regexp) {
const [[regexpString, options]] = Object.entries(regexp);
return { ...options, regexp: regexpString };
}

View File

@ -196,7 +196,7 @@ function applyEmoji(
enableShortcuts,
inlineEmoji,
customEmojiTranslation,
watchedWordsReplacer,
watchedWordsReplace,
emojiDenyList
) {
let result = null;
@ -206,19 +206,16 @@ function applyEmoji(
content = emojiUnicodeReplacer(content);
}
if (watchedWordsReplacer) {
const watchedWordRegex = Object.keys(watchedWordsReplacer);
watchedWordRegex.forEach((watchedWord) => {
if (content?.match(watchedWord)) {
const regex = new RegExp(watchedWord, "g");
if (content && watchedWordsReplace) {
Object.entries(watchedWordsReplace).forEach(([regexpString, options]) => {
if (content.match(regexpString)) {
const regex = new RegExp(regexpString, "g");
const matches = content.match(regex);
const replacement = watchedWordsReplacer[watchedWord].replacement;
matches.forEach(() => {
const matchingRegex = regex.exec(content);
if (matchingRegex) {
content = content.replace(matchingRegex[1], replacement);
content = content.replace(matchingRegex[1], options.replacement);
}
});
}
@ -226,9 +223,9 @@ function applyEmoji(
}
// prevent denied emoji and aliases from being rendered
if (emojiDenyList?.length > 0) {
if (content && emojiDenyList?.length > 0) {
emojiDenyList.forEach((emoji) => {
if (content?.match(emoji)) {
if (content.match(emoji)) {
const regex = new RegExp(`:${emoji}:`, "g");
content = content.replace(regex, "");
}

View File

@ -1,8 +1,3 @@
import {
createWatchedWordRegExp,
toWatchedWord,
} from "discourse-common/utils/watched-words";
const MAX_MATCHES = 100;
function isLinkOpen(str) {
@ -59,11 +54,12 @@ export function setup(helper) {
if (md.options.discourse.watchedWordsReplace) {
Object.entries(md.options.discourse.watchedWordsReplace).forEach(
([regexpString, options]) => {
const word = toWatchedWord({ [regexpString]: options });
matchers.push({
word: new RegExp(options.regexp, options.case_sensitive ? "" : "i"),
pattern: createWatchedWordRegExp(word),
pattern: new RegExp(
regexpString,
options.case_sensitive ? "gu" : "gui"
),
replacement: options.replacement,
link: false,
html: options.html,
@ -75,11 +71,12 @@ export function setup(helper) {
if (md.options.discourse.watchedWordsLink) {
Object.entries(md.options.discourse.watchedWordsLink).forEach(
([regexpString, options]) => {
const word = toWatchedWord({ [regexpString]: options });
matchers.push({
word: new RegExp(options.regexp, options.case_sensitive ? "" : "i"),
pattern: createWatchedWordRegExp(word),
pattern: new RegExp(
regexpString,
options.case_sensitive ? "gu" : "gui"
),
replacement: options.replacement,
link: true,
});

View File

@ -1,13 +1,8 @@
import {
createWatchedWordRegExp,
toWatchedWord,
} from "discourse-common/utils/watched-words";
export function censorFn(regexpList, replacementLetter) {
export function censorFn(regexpList, replacementLetter = "■") {
if (regexpList?.length) {
replacementLetter = replacementLetter || "■";
let censorRegexps = regexpList.map((regexp) => {
return createWatchedWordRegExp(toWatchedWord(regexp));
const censorRegexps = regexpList.map((entry) => {
const [regexp, options] = Object.entries(entry)[0];
return new RegExp(regexp, options.case_sensitive ? "gu" : "gui");
});
return function (text) {

View File

@ -91,7 +91,6 @@ module PrettyText
discourse-common/addon/lib/deprecated
discourse-common/addon/lib/escape
discourse-common/addon/lib/avatar-utils
discourse-common/addon/utils/watched-words
discourse/app/lib/to-markdown
discourse/app/static/markdown-it/features
].each do |f|