@sideid/id-profanity-filter 1.11.6 → 1.11.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/index.d.ts +2 -2
  2. package/dist/index.esm.js +1345 -455
  3. package/dist/index.esm.js.map +1 -1
  4. package/dist/index.js +1345 -455
  5. package/dist/index.js.map +1 -1
  6. package/dist/types/config/options.d.ts +1 -1
  7. package/dist/types/constants/categories/blasphemy.d.ts +1 -1
  8. package/dist/types/constants/categories/disgusting.d.ts +1 -1
  9. package/dist/types/constants/categories/drugs.d.ts +1 -1
  10. package/dist/types/constants/categories/insult.d.ts +1 -1
  11. package/dist/types/constants/categories/profanity.d.ts +1 -1
  12. package/dist/types/constants/categories/sexual.d.ts +1 -1
  13. package/dist/types/constants/categories/slur.d.ts +1 -1
  14. package/dist/types/constants/regions/bali.d.ts +1 -1
  15. package/dist/types/constants/regions/batak.d.ts +1 -1
  16. package/dist/types/constants/regions/betawi.d.ts +1 -1
  17. package/dist/types/constants/regions/general.d.ts +1 -1
  18. package/dist/types/constants/regions/madura.d.ts +4 -0
  19. package/dist/types/constants/regions/minang.d.ts +4 -0
  20. package/dist/types/constants/regions/sunda.d.ts +1 -1
  21. package/dist/types/constants/wordList.d.ts +10 -1
  22. package/dist/types/core/analyzer.d.ts +1 -1
  23. package/dist/types/core/filter.d.ts +1 -1
  24. package/dist/types/core/matcher.d.ts +1 -1
  25. package/dist/types/types/index.d.ts +2 -2
  26. package/dist/types/utils/regexUtils.d.ts +1 -1
  27. package/package.json +1 -1
  28. package/src/config/options.ts +25 -30
  29. package/src/constants/categories/blasphemy.ts +26 -15
  30. package/src/constants/categories/disgusting.ts +62 -54
  31. package/src/constants/categories/drugs.ts +56 -47
  32. package/src/constants/categories/insult.ts +80 -76
  33. package/src/constants/categories/profanity.ts +98 -92
  34. package/src/constants/categories/sexual.ts +74 -65
  35. package/src/constants/categories/slur.ts +73 -66
  36. package/src/constants/regions/aceh.ts +5 -174
  37. package/src/constants/regions/bali.ts +4 -137
  38. package/src/constants/regions/batak.ts +5 -9
  39. package/src/constants/regions/betawi.ts +19 -22
  40. package/src/constants/regions/general.ts +140 -144
  41. package/src/constants/regions/jawa.ts +11 -29
  42. package/src/constants/regions/madura.ts +57 -0
  43. package/src/constants/regions/minang.ts +53 -0
  44. package/src/constants/regions/sunda.ts +343 -19
  45. package/src/constants/wordList.ts +31 -34
  46. package/src/core/analyzer.ts +15 -26
  47. package/src/core/filter.ts +23 -39
  48. package/src/core/matcher.ts +25 -49
  49. package/src/index.ts +5 -16
  50. package/src/types/index.ts +26 -25
  51. package/src/utils/ahoCorasick.ts +1 -1
  52. package/src/utils/regexUtils.ts +37 -43
  53. package/src/utils/similarityUtils.ts +13 -28
  54. package/src/utils/stringUtils.ts +30 -38
  55. /package/{prettierrc → .prettierrc} +0 -0
@@ -1,8 +1,8 @@
1
- import { FilterOptions, FilterResult, ProfanityWord } from "../types";
2
- import { findProfanity, findProfanityWithMetadata } from "./matcher";
3
- import { censorWord, escapeRegExp } from "../utils/stringUtils";
4
- import { createWordRegex } from "../utils/regexUtils";
5
- import { DEFAULT_OPTIONS, makeRandomGrawlixString } from "../config/options";
1
+ import { FilterOptions, FilterResult, ProfanityWord } from '../types';
2
+ import { findProfanity, findProfanityWithMetadata } from './matcher';
3
+ import { censorWord, escapeRegExp } from '../utils/stringUtils';
4
+ import { createWordRegex } from '../utils/regexUtils';
5
+ import { DEFAULT_OPTIONS, makeRandomGrawlixString } from '../config/options';
6
6
 
7
7
  interface FindProfanityFunction {
8
8
  (text: string, options?: FilterOptions): string[];
@@ -16,12 +16,9 @@ interface FindProfanityFunction {
16
16
  * @param options Opsi untuk filter
17
17
  * @returns FilterResult dengan hasil filter
18
18
  */
19
- export function filter(
20
- text: string,
21
- options: FilterOptions = {},
22
- ): FilterResult {
19
+ export function filter(text: string, options: FilterOptions = {}): FilterResult {
23
20
  const {
24
- replaceWith = "*",
21
+ replaceWith = '*',
25
22
  fullWordCensor = true,
26
23
  detectLeetSpeak = true,
27
24
  whitelist = [],
@@ -74,10 +71,7 @@ export function filter(
74
71
  const metadata = matchDetails.find(
75
72
  (m) =>
76
73
  m.word.toLowerCase() === word.toLowerCase() ||
77
- (m.aliases &&
78
- m.aliases.some(
79
- (alias) => alias.toLowerCase() === word.toLowerCase(),
80
- )),
74
+ (m.aliases && m.aliases.some((alias) => alias.toLowerCase() === word.toLowerCase()))
81
75
  );
82
76
 
83
77
  const variants = actualMatches.get(word.toLowerCase()) || [];
@@ -86,7 +80,7 @@ export function filter(
86
80
  const uniqueVariants = [...new Set(variants)];
87
81
 
88
82
  uniqueVariants.forEach((variant) => {
89
- const regex = new RegExp(`\\b${escapeRegExp(variant)}\\b`, "gi");
83
+ const regex = new RegExp(`\\b${escapeRegExp(variant)}\\b`, 'gi');
90
84
 
91
85
  let match;
92
86
  while ((match = regex.exec(filteredText)) !== null) {
@@ -98,11 +92,7 @@ export function filter(
98
92
  if (useRandomGrawlix) {
99
93
  censoredWord = makeRandomGrawlixString(originalWord.length);
100
94
  } else {
101
- censoredWord = censorWord(
102
- originalWord,
103
- replaceWith,
104
- !fullWordCensor && keepFirstAndLast,
105
- );
95
+ censoredWord = censorWord(originalWord, replaceWith, !fullWordCensor && keepFirstAndLast);
106
96
  }
107
97
 
108
98
  replacements.push({
@@ -112,8 +102,8 @@ export function filter(
112
102
  });
113
103
 
114
104
  filteredText = filteredText.replace(
115
- new RegExp(`\\b${escapeRegExp(originalWord)}\\b`, "g"),
116
- censoredWord,
105
+ new RegExp(`\\b${escapeRegExp(originalWord)}\\b`, 'g'),
106
+ censoredWord
117
107
  );
118
108
  }
119
109
  });
@@ -141,7 +131,7 @@ export function filter(
141
131
  censoredWord = censorWord(
142
132
  originalWord,
143
133
  replaceWith,
144
- !fullWordCensor && keepFirstAndLast,
134
+ !fullWordCensor && keepFirstAndLast
145
135
  );
146
136
  }
147
137
 
@@ -152,8 +142,8 @@ export function filter(
152
142
  });
153
143
 
154
144
  filteredText = filteredText.replace(
155
- new RegExp(escapeRegExp(originalWord), "g"),
156
- censoredWord,
145
+ new RegExp(escapeRegExp(originalWord), 'g'),
146
+ censoredWord
157
147
  );
158
148
  }
159
149
  }
@@ -180,7 +170,7 @@ export function filter(
180
170
  censoredWord = censorWord(
181
171
  originalWord,
182
172
  replaceWith,
183
- !fullWordCensor && keepFirstAndLast,
173
+ !fullWordCensor && keepFirstAndLast
184
174
  );
185
175
  }
186
176
 
@@ -191,8 +181,8 @@ export function filter(
191
181
  });
192
182
 
193
183
  filteredText = filteredText.replace(
194
- new RegExp(escapeRegExp(originalWord), "g"),
195
- censoredWord,
184
+ new RegExp(escapeRegExp(originalWord), 'g'),
185
+ censoredWord
196
186
  );
197
187
  }
198
188
  }
@@ -204,19 +194,13 @@ export function filter(
204
194
  const metadata = matchDetails.find(
205
195
  (m) =>
206
196
  m.word.toLowerCase() === word.toLowerCase() ||
207
- (m.aliases &&
208
- m.aliases.some(
209
- (alias) => alias.toLowerCase() === word.toLowerCase(),
210
- )),
197
+ (m.aliases && m.aliases.some((alias) => alias.toLowerCase() === word.toLowerCase()))
211
198
  );
212
199
 
213
200
  const variants = actualMatches.get(word.toLowerCase()) || [];
214
201
 
215
202
  variants.forEach((variant) => {
216
- const exactVariantRegex = new RegExp(
217
- `\\b${escapeRegExp(variant)}\\b`,
218
- "gi",
219
- );
203
+ const exactVariantRegex = new RegExp(`\\b${escapeRegExp(variant)}\\b`, 'gi');
220
204
 
221
205
  let match;
222
206
  while ((match = exactVariantRegex.exec(filteredText)) !== null) {
@@ -231,7 +215,7 @@ export function filter(
231
215
  censoredWord = censorWord(
232
216
  originalWord,
233
217
  replaceWith,
234
- !fullWordCensor && keepFirstAndLast,
218
+ !fullWordCensor && keepFirstAndLast
235
219
  );
236
220
  }
237
221
 
@@ -242,8 +226,8 @@ export function filter(
242
226
  });
243
227
 
244
228
  filteredText = filteredText.replace(
245
- new RegExp(`\\b${escapeRegExp(originalWord)}\\b`, "g"),
246
- censoredWord,
229
+ new RegExp(`\\b${escapeRegExp(originalWord)}\\b`, 'g'),
230
+ censoredWord
247
231
  );
248
232
  }
249
233
  });
@@ -1,19 +1,14 @@
1
- import {
2
- ProfanityWord,
3
- ProfanityCategory,
4
- Region,
5
- FilterOptions,
6
- } from "../types";
7
-
8
- import { wordObjects } from "../constants/wordList";
9
- import { normalizeText } from "../utils/stringUtils";
10
- import { createWordRegex } from "../utils/regexUtils";
1
+ import { ProfanityWord, ProfanityCategory, Region, FilterOptions } from '../types';
2
+
3
+ import { wordObjects } from '../constants/wordList';
4
+ import { normalizeText } from '../utils/stringUtils';
5
+ import { createWordRegex } from '../utils/regexUtils';
11
6
  import {
12
7
  findPossibleProfanityBySimiliarity,
13
8
  findProfanityByLevenshteinDistance,
14
- } from "../utils/similarityUtils";
15
- import { DEFAULT_OPTIONS } from "../config/options";
16
- import { AhoCorasick } from "../utils/ahoCorasick";
9
+ } from '../utils/similarityUtils';
10
+ import { DEFAULT_OPTIONS } from '../config/options';
11
+ import { AhoCorasick } from '../utils/ahoCorasick';
17
12
 
18
13
  const globalAhoCorasick = new AhoCorasick();
19
14
  let ahoCorasickInitialized = false;
@@ -34,10 +29,7 @@ interface FindProfanityFunction {
34
29
  lastActualMatches?: Map<string, string[]>;
35
30
  }
36
31
 
37
- export function findProfanity(
38
- text: string,
39
- options: FilterOptions = {},
40
- ): string[] {
32
+ export function findProfanity(text: string, options: FilterOptions = {}): string[] {
41
33
  const {
42
34
  wordList = [],
43
35
  detectLeetSpeak = true,
@@ -60,9 +52,7 @@ export function findProfanity(
60
52
 
61
53
  if (baseWordsToCheck.length === 0) {
62
54
  const filteredWords = wordObjects.filter((word) => {
63
- const matchCategory = categories
64
- ? categories.includes(word.category)
65
- : true;
55
+ const matchCategory = categories ? categories.includes(word.category) : true;
66
56
  const matchRegion = regions ? regions.includes(word.region) : true;
67
57
  const matchSeverity = word.severity >= severityThreshold;
68
58
  return matchCategory && matchRegion && matchSeverity;
@@ -74,9 +64,7 @@ export function findProfanity(
74
64
  const aliasMap = new Map<string, string>();
75
65
  wordObjects.forEach((wordObj) => {
76
66
  if (wordObj.aliases && wordObj.aliases.length > 0) {
77
- const matchCategory = categories
78
- ? categories.includes(wordObj.category)
79
- : true;
67
+ const matchCategory = categories ? categories.includes(wordObj.category) : true;
80
68
  const matchRegion = regions ? regions.includes(wordObj.region) : true;
81
69
  const matchSeverity = wordObj.severity >= severityThreshold;
82
70
 
@@ -88,10 +76,9 @@ export function findProfanity(
88
76
  }
89
77
  });
90
78
 
91
- const wordsToCheck = [
92
- ...baseWordsToCheck,
93
- ...Array.from(aliasMap.keys()),
94
- ].filter((word) => !whitelist.includes(word.toLowerCase()));
79
+ const wordsToCheck = [...baseWordsToCheck, ...Array.from(aliasMap.keys())].filter(
80
+ (word) => !whitelist.includes(word.toLowerCase())
81
+ );
95
82
 
96
83
  if (wordsToCheck.length === 0) {
97
84
  return [];
@@ -104,8 +91,7 @@ export function findProfanity(
104
91
 
105
92
  const basicMatches = globalAhoCorasick.searchUnique(normalizedText);
106
93
  for (const match of basicMatches) {
107
- const originalWord =
108
- aliasMap.get(match.toLowerCase()) || match.toLowerCase();
94
+ const originalWord = aliasMap.get(match.toLowerCase()) || match.toLowerCase();
109
95
  matches.add(originalWord);
110
96
 
111
97
  if (!actualMatches.has(originalWord)) {
@@ -126,8 +112,7 @@ export function findProfanity(
126
112
 
127
113
  let match;
128
114
  while ((match = leetRegex.exec(text)) !== null) {
129
- const originalWord =
130
- aliasMap.get(word.toLowerCase()) || word.toLowerCase();
115
+ const originalWord = aliasMap.get(word.toLowerCase()) || word.toLowerCase();
131
116
  matches.add(originalWord);
132
117
 
133
118
  if (!actualMatches.has(originalWord)) {
@@ -150,8 +135,7 @@ export function findProfanity(
150
135
 
151
136
  let match;
152
137
  while ((match = variantRegex.exec(text)) !== null) {
153
- const originalWord =
154
- aliasMap.get(word.toLowerCase()) || word.toLowerCase();
138
+ const originalWord = aliasMap.get(word.toLowerCase()) || word.toLowerCase();
155
139
  matches.add(originalWord);
156
140
 
157
141
  if (!actualMatches.has(originalWord)) {
@@ -174,8 +158,7 @@ export function findProfanity(
174
158
 
175
159
  let match;
176
160
  while ((match = splitRegex.exec(text)) !== null) {
177
- const originalWord =
178
- aliasMap.get(word.toLowerCase()) || word.toLowerCase();
161
+ const originalWord = aliasMap.get(word.toLowerCase()) || word.toLowerCase();
179
162
  matches.add(originalWord);
180
163
 
181
164
  if (!actualMatches.has(originalWord)) {
@@ -192,13 +175,12 @@ export function findProfanity(
192
175
  text,
193
176
  wordsToCheck,
194
177
  similarityThreshold,
195
- maxLevenshteinDistance,
178
+ maxLevenshteinDistance
196
179
  );
197
180
 
198
181
  possibleProfanity.forEach((item) => {
199
182
  const originalWord =
200
- aliasMap.get(item.original.toLowerCase()) ||
201
- item.original.toLowerCase();
183
+ aliasMap.get(item.original.toLowerCase()) || item.original.toLowerCase();
202
184
  matches.add(originalWord);
203
185
 
204
186
  if (!actualMatches.has(originalWord)) {
@@ -210,15 +192,14 @@ export function findProfanity(
210
192
  const possibleProfanity = findPossibleProfanityBySimiliarity(
211
193
  text,
212
194
  wordsToCheck,
213
- similarityThreshold,
195
+ similarityThreshold
214
196
  );
215
197
 
216
198
  possibleProfanity.forEach((item) => {
217
199
  matches.add(item.original.toLowerCase());
218
200
 
219
201
  const originalWord =
220
- aliasMap.get(item.original.toLowerCase()) ||
221
- item.original.toLowerCase();
202
+ aliasMap.get(item.original.toLowerCase()) || item.original.toLowerCase();
222
203
  if (!actualMatches.has(originalWord)) {
223
204
  actualMatches.set(originalWord, []);
224
205
  }
@@ -241,7 +222,7 @@ export function findProfanity(
241
222
  */
242
223
  export function findProfanityWithMetadata(
243
224
  text: string,
244
- options: FilterOptions = {},
225
+ options: FilterOptions = {}
245
226
  ): ProfanityWord[] {
246
227
  const matches = findProfanity(text, options);
247
228
  if (matches.length === 0) {
@@ -253,10 +234,7 @@ export function findProfanityWithMetadata(
253
234
  const wordObject = wordObjects.find(
254
235
  (obj) =>
255
236
  obj.word.toLowerCase() === word.toLowerCase() ||
256
- (obj.aliases &&
257
- obj.aliases.some(
258
- (alias) => alias.toLowerCase() === word.toLowerCase(),
259
- )),
237
+ (obj.aliases && obj.aliases.some((alias) => alias.toLowerCase() === word.toLowerCase()))
260
238
  );
261
239
 
262
240
  return wordObject;
@@ -270,9 +248,7 @@ export function findProfanityWithMetadata(
270
248
  * @param matchDetails Hasil pencarian dari fingProfanityWithMetadata()
271
249
  * @return Array kategori unik
272
250
  */
273
- export function findCategories(
274
- matchDetails: ProfanityWord[],
275
- ): ProfanityCategory[] {
251
+ export function findCategories(matchDetails: ProfanityWord[]): ProfanityCategory[] {
276
252
  const categories = new Set<ProfanityCategory>();
277
253
 
278
254
  matchDetails.forEach((word) => {
package/src/index.ts CHANGED
@@ -8,12 +8,7 @@ export * from './utils/similarityUtils';
8
8
  export * from './config/options';
9
9
 
10
10
  import { filter, isProfane } from './core/filter';
11
- import {
12
- analyze,
13
- batchAnalyze,
14
- analyzeBySentence,
15
- analyzeWithContext,
16
- } from './core/analyzer';
11
+ import { analyze, batchAnalyze, analyzeBySentence, analyzeWithContext } from './core/analyzer';
17
12
  import { FilterOptions, FilterResult, AnalysisResult } from './types';
18
13
  import {
19
14
  DEFAULT_OPTIONS,
@@ -105,10 +100,7 @@ export class IDProfanityFilter {
105
100
  * @param presetName Nama preset yang akan digunakan
106
101
  * @param additionalOptions Opsi tambahan untuk override
107
102
  */
108
- usePreset(
109
- presetName: string,
110
- additionalOptions: Partial<FilterOptions> = {},
111
- ) {
103
+ usePreset(presetName: string, additionalOptions: Partial<FilterOptions> = {}) {
112
104
  this.options = getPresetOptions(presetName, additionalOptions);
113
105
  }
114
106
 
@@ -125,10 +117,7 @@ export class IDProfanityFilter {
125
117
  * @param word Kata yang akan diabaikan
126
118
  */
127
119
  addToWhitelist(word: string) {
128
- this.options.whitelist = [
129
- ...(this.options.whitelist || []),
130
- word.toLowerCase(),
131
- ];
120
+ this.options.whitelist = [...(this.options.whitelist || []), word.toLowerCase()];
132
121
  }
133
122
 
134
123
  /**
@@ -139,7 +128,7 @@ export class IDProfanityFilter {
139
128
  if (!this.options.whitelist) return;
140
129
 
141
130
  this.options.whitelist = this.options.whitelist.filter(
142
- (w) => w.toLowerCase() !== word.toLowerCase(),
131
+ (w) => w.toLowerCase() !== word.toLowerCase()
143
132
  );
144
133
  }
145
134
 
@@ -166,7 +155,7 @@ export class IDProfanityFilter {
166
155
  enableSimilarityDetection(
167
156
  threshold: number = 0.8,
168
157
  useLevenshtein: boolean = false,
169
- maxLevenshteinDistance: number = 2,
158
+ maxLevenshteinDistance: number = 2
170
159
  ) {
171
160
  this.options.detectSimilarity = true;
172
161
  this.options.similarityThreshold = threshold;
@@ -1,31 +1,32 @@
1
1
  export type ProfanityCategory =
2
- | "sexual"
3
- | "insult"
4
- | "profanity"
5
- | "slur"
6
- | "drugs"
7
- | "disgusting"
8
- | "blasphemy";
2
+ | 'sexual'
3
+ | 'insult'
4
+ | 'profanity'
5
+ | 'slur'
6
+ | 'drugs'
7
+ | 'disgusting'
8
+ | 'blasphemy';
9
9
 
10
10
  export type Region =
11
- | "general"
12
- | "jawa"
13
- | "sunda"
14
- | "betawi"
15
- | "batak"
16
- | "minang"
17
- | "bali"
18
- | "madura"
19
- | "bugis"
20
- | "aceh"
21
- | "ambon"
22
- | "papua"
23
- | "manado"
24
- | "banjar"
25
- | "palembang"
26
- | "lampung"
27
- | "ntt"
28
- | "ntb";
11
+ | 'general'
12
+ | 'jawa'
13
+ | 'sunda'
14
+ | 'betawi'
15
+ | 'batak'
16
+ | 'minang'
17
+ | 'bali'
18
+ | 'madura'
19
+ | 'bugis'
20
+ | 'aceh'
21
+ | 'ambon'
22
+ | 'papua'
23
+ | 'manado'
24
+ | 'banjar'
25
+ | 'palembang'
26
+ | 'lampung'
27
+ | 'ntt'
28
+ | 'mandailing'
29
+ | 'ntb';
29
30
 
30
31
  export interface ProfanityWord {
31
32
  word: string;
@@ -30,7 +30,7 @@ export class AhoCorasick {
30
30
  */
31
31
  addPattern(pattern: string): void {
32
32
  if (this.built) {
33
- throw new Error("Cannot add patterns after the automaton is built");
33
+ throw new Error('Cannot add patterns after the automaton is built');
34
34
  }
35
35
 
36
36
  let node = this.root;
@@ -1,5 +1,5 @@
1
- import { escapeRegExp } from "./stringUtils";
2
- import { RegexOptions } from "../types";
1
+ import { escapeRegExp } from './stringUtils';
2
+ import { RegexOptions } from '../types';
3
3
 
4
4
  /**
5
5
  * Membuat pola regex untuk mencocokkan kata
@@ -8,10 +8,7 @@ import { RegexOptions } from "../types";
8
8
  * @param options Opsi untuk pembuatan regex
9
9
  * @returns Objek RegExp
10
10
  */
11
- export function createWordRegex(
12
- word: string,
13
- options: RegexOptions = {},
14
- ): RegExp {
11
+ export function createWordRegex(word: string, options: RegexOptions = {}): RegExp {
15
12
  const {
16
13
  wholeWord = true,
17
14
  caseSensitive = false,
@@ -44,7 +41,7 @@ export function createWordRegex(
44
41
  }
45
42
 
46
43
  // Buat regex dengan flag case-insensitive jika diminta
47
- return new RegExp(pattern, caseSensitive ? "g" : "gi");
44
+ return new RegExp(pattern, caseSensitive ? 'g' : 'gi');
48
45
  }
49
46
 
50
47
  /**
@@ -59,32 +56,32 @@ export function createWordRegex(
59
56
  */
60
57
  export function addLeetSpeakVariations(pattern: string): string {
61
58
  const leetMap: Record<string, string[]> = {
62
- a: ["a", "4", "@"],
63
- b: ["b", "8", "6"],
64
- c: ["c", "(", "{", "<"],
65
- e: ["e", "3"],
66
- g: ["g", "6", "9"],
67
- i: ["i", "1", "!", "|"],
68
- l: ["l", "1", "|"],
69
- o: ["o", "0"],
70
- s: ["s", "5", "$"],
71
- t: ["t", "7", "+"],
72
- z: ["z", "2"],
59
+ a: ['a', '4', '@'],
60
+ b: ['b', '8', '6'],
61
+ c: ['c', '(', '{', '<'],
62
+ e: ['e', '3'],
63
+ g: ['g', '6', '9'],
64
+ i: ['i', '1', '!', '|'],
65
+ l: ['l', '1', '|'],
66
+ o: ['o', '0'],
67
+ s: ['s', '5', '$'],
68
+ t: ['t', '7', '+'],
69
+ z: ['z', '2'],
73
70
  };
74
71
 
75
72
  return pattern
76
- .split("")
73
+ .split('')
77
74
  .map((char) => {
78
75
  const lowerChar = char.toLowerCase();
79
76
  const variations = leetMap[lowerChar];
80
77
 
81
78
  if (variations && variations.length > 1) {
82
- return `[${variations.join("")}]`;
79
+ return `[${variations.join('')}]`;
83
80
  }
84
81
 
85
82
  return char;
86
83
  })
87
- .join("");
84
+ .join('');
88
85
  }
89
86
 
90
87
  /**
@@ -95,7 +92,7 @@ export function addLeetSpeakVariations(pattern: string): string {
95
92
  */
96
93
  export function addSplitVariations(pattern: string): string {
97
94
  // Tambahkan kemungkinan spasi atau karakter penghubung di antara setiap huruf
98
- return pattern.split("").join("[\\s\\-._*+]?");
95
+ return pattern.split('').join('[\\s\\-._*+]?');
99
96
  }
100
97
 
101
98
  /**
@@ -109,7 +106,7 @@ export function createEvasionRegex(word: string): RegExp {
109
106
  // Tambahkan kemungkinan spasi atau karakter penghubung di antara setiap huruf
110
107
  const pattern = addSplitVariations(escapeRegExp(word));
111
108
 
112
- return new RegExp(pattern, "gi");
109
+ return new RegExp(pattern, 'gi');
113
110
  }
114
111
 
115
112
  /**
@@ -121,30 +118,30 @@ export function createEvasionRegex(word: string): RegExp {
121
118
  export function addIndonesianVariations(pattern: string): string {
122
119
  // Variasi ejaan dalam Bahasa Indonesia
123
120
  const variationMap: Record<string, string[]> = {
124
- c: ["c", "k"], // contoh: becok/bekok
125
- k: ["k", "c", "q"], // contoh: kacau/qacau
126
- j: ["j", "dj"], // contoh: jualan/djualan (ejaan lama)
127
- y: ["y", "j"], // contoh: ya/ja
128
- u: ["u", "oe"], // contoh: untuk/oentoek (ejaan lama)
129
- f: ["f", "p", "v"], // contoh: kafir/kapir
130
- z: ["z", "j", "s"], // contoh: zaman/jaman
131
- x: ["x", "ks"], // contoh: taxi/taksi
121
+ c: ['c', 'k'], // contoh: becok/bekok
122
+ k: ['k', 'c', 'q'], // contoh: kacau/qacau
123
+ j: ['j', 'dj'], // contoh: jualan/djualan (ejaan lama)
124
+ y: ['y', 'j'], // contoh: ya/ja
125
+ u: ['u', 'oe'], // contoh: untuk/oentoek (ejaan lama)
126
+ f: ['f', 'p', 'v'], // contoh: kafir/kapir
127
+ z: ['z', 'j', 's'], // contoh: zaman/jaman
128
+ x: ['x', 'ks'], // contoh: taxi/taksi
132
129
  };
133
130
 
134
131
  // Ganti tiap karakter dengan variasinya
135
132
  return pattern
136
- .split("")
133
+ .split('')
137
134
  .map((char) => {
138
135
  const lowerChar = char.toLowerCase();
139
136
  const variations = variationMap[lowerChar];
140
137
 
141
138
  if (variations && variations.length > 1) {
142
- return `[${variations.join("")}]`;
139
+ return `[${variations.join('')}]`;
143
140
  }
144
141
 
145
142
  return char;
146
143
  })
147
- .join("");
144
+ .join('');
148
145
  }
149
146
 
150
147
  /**
@@ -155,7 +152,7 @@ export function addIndonesianVariations(pattern: string): string {
155
152
  */
156
153
  export function createIndonesianVariationRegex(word: string): RegExp {
157
154
  const pattern = addIndonesianVariations(escapeRegExp(word));
158
- return new RegExp(`\\b${pattern}\\b`, "gi");
155
+ return new RegExp(`\\b${pattern}\\b`, 'gi');
159
156
  }
160
157
 
161
158
  /**
@@ -165,16 +162,13 @@ export function createIndonesianVariationRegex(word: string): RegExp {
165
162
  * @param contextSize Jumlah kata konteks sebelum dan sesudah
166
163
  * @returns Objek RegExp
167
164
  */
168
- export function createContextRegex(
169
- word: string,
170
- contextSize: number = 3,
171
- ): RegExp {
165
+ export function createContextRegex(word: string, contextSize: number = 3): RegExp {
172
166
  const wordPattern = escapeRegExp(word);
173
167
 
174
168
  // Membuat pola yang menangkap beberapa kata sebelum dan setelah kata target
175
169
  const pattern = `((?:\\S+\\s+){0,${contextSize}})(\\b${wordPattern}\\b)((?:\\s+\\S+){0,${contextSize}})`;
176
170
 
177
- return new RegExp(pattern, "gi");
171
+ return new RegExp(pattern, 'gi');
178
172
  }
179
173
 
180
174
  /**
@@ -186,8 +180,8 @@ export function createContextRegex(
186
180
  export function createWordFormRegex(word: string): RegExp {
187
181
  // Implementasi sederhana untuk mencocokkan berbagai imbuhan
188
182
  // Ini bisa dikembangkan lebih lanjut untuk mencocokkan bentukan kata yang lebih kompleks
189
- const prefixes = ["", "me", "pe", "ber", "di", "ter", "se"];
190
- const suffixes = ["", "kan", "an", "i", "nya"];
183
+ const prefixes = ['', 'me', 'pe', 'ber', 'di', 'ter', 'se'];
184
+ const suffixes = ['', 'kan', 'an', 'i', 'nya'];
191
185
 
192
186
  const patterns = [];
193
187
 
@@ -198,5 +192,5 @@ export function createWordFormRegex(word: string): RegExp {
198
192
  }
199
193
  }
200
194
 
201
- return new RegExp(patterns.join("|"), "gi");
195
+ return new RegExp(patterns.join('|'), 'gi');
202
196
  }