-
Notifications
You must be signed in to change notification settings - Fork 13
Description
Summary
Several hot paths in profanity.ts allocate unnecessary objects, recreate regexes, or perform O(n·m) string operations that can be replaced with O(n) alternatives. These changes target the core exists(), censor(), and isWhitelisted() methods.
Proposed Changes
1. Array-based string building in replaceProfanity
Current: Each matched word triggers result.slice(…) + censoredWord + result.slice(…), copying the entire result string per match — O(n·m) where m is the number of matches.
Proposed: Collect text segments and censored words into an array, then join("") once at the end — O(n).
2. Hoist vowel regexes to module-level constants
Current: new RegExp("[aeiou]", …) is constructed on every matched profane word when using CensorType.FirstVowel or CensorType.AllVowels.
Proposed: Create FIRST_VOWEL_RE and ALL_VOWELS_RE as module-level constants, reused across all calls.
3. Hoist word-boundary character regexes to module-level constants
Current: isWhitelisted recreates an isWordChar closure (and its regex) on every invocation.
Proposed: Create UNICODE_WORD_CHAR_RE and ASCII_WORD_CHAR_RE as module-level constants. Select the appropriate one once per isWhitelisted call instead of per-character.
4. Reduce allocations in getRegex
Current: [...new Set(languages.map(…))] → .toSorted() → .flatMap() → .filter() creates multiple intermediate arrays and closures.
Proposed: Single-pass dedup loop with in-place sort(), and a for…of loop with push to build the word list — fewer intermediate arrays and zero closure allocations.
Impact
These changes affect the most frequently called methods (exists, censor) and scale with input size and number of profane-word matches. The improvements are most noticeable with large texts or high match counts.