diff --git a/backend/__tests__/utils/misc.spec.ts b/backend/__tests__/utils/misc.spec.ts index 89707c7f6404..4101dfe70a25 100644 --- a/backend/__tests__/utils/misc.spec.ts +++ b/backend/__tests__/utils/misc.spec.ts @@ -197,43 +197,6 @@ describe("Misc Utils", () => { ); }); - it("sanitizeString", () => { - const testCases = [ - { - input: "h̶̼͔̭͈̏́̀́͋͜ͅe̵̺̞̦̫̫͔̋́̅̅̃̀͝͝ļ̶̬̯͚͇̺͍̞̫̟͖͋̓͛̆̒̓͜ĺ̴̗̘͇̬̆͂͌̈͊͝͝ỡ̴̡̦̩̠̞̐̃͆̚͠͝", - expected: "hello", - }, - { - input: "hello", - expected: "hello", - }, - { - input: "hel lo", - expected: "hel lo", - }, - { - input: " hel lo ", - expected: "hel lo", - }, - { - input: "", - expected: "", - }, - { - input: " \n\n\n", - expected: "", - }, - { - input: undefined, - expected: undefined, - }, - ]; - - testCases.forEach(({ input, expected }) => { - expect(Misc.sanitizeString(input)).toEqual(expected); - }); - }); - it("getOrdinalNumberString", () => { const testCases = [ { diff --git a/backend/src/utils/misc.ts b/backend/src/utils/misc.ts index 4451e8eb3ff2..00dd9c2e1609 100644 --- a/backend/src/utils/misc.ts +++ b/backend/src/utils/misc.ts @@ -1,5 +1,6 @@ import { MILLISECONDS_IN_DAY } from "@monkeytype/util/date-and-time"; import { roundTo2 } from "@monkeytype/util/numbers"; +export { sanitizeString } from "@monkeytype/util/strings"; import uaparser from "ua-parser-js"; import { MonkeyRequest } from "../api/types"; import { ObjectId } from "mongodb"; @@ -115,18 +116,6 @@ export function flattenObjectDeep( return result; } -export function sanitizeString(str: string | undefined): string | undefined { - if (str === undefined || str === "") { - return str; - } - - return str - .replace(/[\u0300-\u036F]/g, "") - .trim() - .replace(/\n{3,}/g, "\n\n") - .replace(/\s{3,}/g, " "); -} - const suffixes = ["th", "st", "nd", "rd"]; export function getOrdinalNumberString(number: number): string { diff --git a/packages/schemas/package.json b/packages/schemas/package.json index df4d9f399de4..47da8c47edeb 100644 --- a/packages/schemas/package.json +++ b/packages/schemas/package.json @@ -22,6 +22,9 @@ "lint": "oxlint . --type-aware --type-check", "lint-fast": "oxlint ." }, + "dependencies": { + "@monkeytype/util": "workspace:*" + }, "devDependencies": { "@monkeytype/tsup-config": "workspace:*", "@monkeytype/typescript-config": "workspace:*", diff --git a/packages/schemas/src/validation/validation.ts b/packages/schemas/src/validation/validation.ts index d573c06cdcea..d5f708601c43 100644 --- a/packages/schemas/src/validation/validation.ts +++ b/packages/schemas/src/validation/validation.ts @@ -1,4 +1,5 @@ import { replaceHomoglyphs } from "./homoglyphs"; +import { sanitizeString } from "@monkeytype/util/strings"; import { ZodEffects, ZodString } from "zod"; // Sorry for the bad words @@ -391,18 +392,6 @@ const disallowedWords = [ "zabourah", ]; -function sanitizeString(str: string | undefined): string | undefined { - if (str === undefined || str === "") { - return str; - } - - return str - .replace(/[\u0300-\u036F]/g, "") - .trim() - .replace(/\n{3,}/g, "\n\n") - .replace(/\s{3,}/g, " "); -} - function containsDisallowedWords( text: string, mode: "word" | "substring", diff --git a/packages/util/__test__/strings.spec.ts b/packages/util/__test__/strings.spec.ts index 9ce93dc6ccb0..47f12048b86a 100644 --- a/packages/util/__test__/strings.spec.ts +++ b/packages/util/__test__/strings.spec.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { kebabToCamelCase } from "../src/strings"; +import { kebabToCamelCase, sanitizeString } from "../src/strings"; describe("strings", () => { describe("kebabToCamelCase", () => { @@ -11,4 +11,56 @@ describe("strings", () => { ).toEqual("oneTwoThreeFourFiveSixSevenEightNineTen"); }); }); + + describe("sanitizeString", () => { + const testCases = [ + { + input: "h̶̼͔̭͈̏́̀́͋͜ͅe̵̺̞̦̫̫͔̋́̅̅̃̀͝͝ļ̶̬̯͚͇̺͍̞̫̟͖͋̓͛̆̒̓͜ĺ̴̗̘͇̬̆͂͌̈͊͝͝ỡ̴̡̦̩̠̞̐̃͆̚͠͝", + expected: "hello", + }, + { + input: "hello", + expected: "hello", + }, + { + input: "hel lo", + expected: "hel lo", + }, + { + input: " hel lo ", + expected: "hel lo", + }, + { + input: "", + expected: "", + }, + { + input: " \n\n\n", + expected: "", + }, + { + input: undefined, + expected: undefined, + }, + { + input: "hello\r\n\r\nworld", + expected: "hello\r\n\r\nworld", + }, + { + input: "hello\n\nworld", + expected: "hello\n\nworld", + }, + { + input: "test \r\n test", + expected: "test \r\n test", + }, + ]; + + it.each(testCases)( + "sanitizeString with input '$input' expects '$expected'", + ({ input, expected }) => { + expect(sanitizeString(input)).toEqual(expected); + }, + ); + }); }); diff --git a/packages/util/src/strings.ts b/packages/util/src/strings.ts index 35d53794eac5..d7f10db3b0a1 100644 --- a/packages/util/src/strings.ts +++ b/packages/util/src/strings.ts @@ -1,3 +1,15 @@ export function kebabToCamelCase(kebab: string): string { return kebab.replace(/-([a-z])/g, (_, char: string) => char.toUpperCase()); } + +export function sanitizeString(str: string | undefined): string | undefined { + if (str === undefined || str === "") { + return str; + } + + return str + .replace(/[\u0300-\u036F]/g, "") + .trim() + .replace(/\n{3,}/g, "\n\n") + .replace(/[^\S\r\n]{3,}/g, " "); +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fcff95bbd8cd..5f6102d1d9ae 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -749,6 +749,9 @@ importers: packages/schemas: dependencies: + '@monkeytype/util': + specifier: workspace:* + version: link:../util zod: specifier: 3.23.8 version: 3.23.8