From 46a06d78df112a5ba9b599283dfe7a60abea901f Mon Sep 17 00:00:00 2001 From: galiprandi <20272796+galiprandi@users.noreply.github.com> Date: Sat, 6 Jun 2026 04:36:00 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Quality:=20Improve=20robustness=20-?= =?UTF-8?q?=20string=20transformations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add `.trim()` to `camelCase` and `pascalCase` transformations in `valueTransforms`. - Fixes a bug where leading spaces caused incorrect capitalization in `camelCase`. - Add test cases for leading/trailing spaces in `strings.test.ts`. - Standardize code style in `strings.ts` to match project conventions. --- lib/utilities/strings.test.ts | 6 ++ lib/utilities/strings.ts | 183 ++++++++++++++++++---------------- 2 files changed, 101 insertions(+), 88 deletions(-) diff --git a/lib/utilities/strings.test.ts b/lib/utilities/strings.test.ts index 406c3c9..3121c5d 100644 --- a/lib/utilities/strings.test.ts +++ b/lib/utilities/strings.test.ts @@ -37,6 +37,9 @@ describe('valueTransforms', () => { expect(valueTransforms('hello world', 'camelCase')).toBe('helloWorld') expect(valueTransforms('hello-world', 'camelCase')).toBe('helloWorld') expect(valueTransforms('hello_world', 'camelCase')).toBe('helloWorld') + expect(valueTransforms(' hello world ', 'camelCase')).toBe( + 'helloWorld', + ) }) it('should transform to pascal case', () => { @@ -44,6 +47,9 @@ describe('valueTransforms', () => { expect(valueTransforms('hello-world', 'pascalCase')).toBe('HelloWorld') expect(valueTransforms('hello_world', 'pascalCase')).toBe('HelloWorld') expect(valueTransforms('helloWorld', 'pascalCase')).toBe('HelloWorld') + expect(valueTransforms(' hello world ', 'pascalCase')).toBe( + 'HelloWorld', + ) }) it('should transform to kebab case', () => { diff --git a/lib/utilities/strings.ts b/lib/utilities/strings.ts index 9722e2a..2bcad35 100644 --- a/lib/utilities/strings.ts +++ b/lib/utilities/strings.ts @@ -2,19 +2,19 @@ * Available transformation types for strings. */ export type TransformType = - | "toUpperCase" - | "toLowerCase" - | "capitalize" - | "titleCase" - | "snakeCase" - | "camelCase" - | "pascalCase" - | "kebabCase" - | "onlyNumbers" - | "onlyLetters" - | "onlyEmail" - | "onlyAlphanumeric" - | "slugify"; + | 'toUpperCase' + | 'toLowerCase' + | 'capitalize' + | 'titleCase' + | 'snakeCase' + | 'camelCase' + | 'pascalCase' + | 'kebabCase' + | 'onlyNumbers' + | 'onlyLetters' + | 'onlyEmail' + | 'onlyAlphanumeric' + | 'slugify' /** * Transforms a string based on the specified transformation type. @@ -42,82 +42,89 @@ export type TransformType = * ``` */ export const valueTransforms = ( - value: string, - transform?: TransformType | TransformType[], + value: string, + transform?: TransformType | TransformType[], ): string => { - const applyTransform = (val: string, t: string): string => { - switch (t) { - case "toUpperCase": - return val.toUpperCase(); - case "toLowerCase": - return val.toLowerCase(); - case "capitalize": { - const trimmed = val.trim(); - return trimmed.charAt(0).toUpperCase() + trimmed.slice(1).toLowerCase(); - } - case "titleCase": - return val - .replace(/([a-z])([A-Z])/g, "$1 $2") - .replace(/[_-]/g, " ") - .toLowerCase() - .trim() - .replace(/\s+/g, " ") - .replace(/\b\w/g, (ch) => ch.toUpperCase()); - case "snakeCase": - return val - .replace(/([a-z])([A-Z])/g, "$1 $2") - .replace(/[_-]/g, " ") - .toLowerCase() - .trim() - .replace(/\s+/g, "_"); - case "camelCase": - return val - .replace(/([a-z])([A-Z])/g, "$1 $2") - .replace(/[_-]/g, " ") - .toLowerCase() - .replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) => - index === 0 ? word.toLowerCase() : word.toUpperCase(), - ) - .replace(/\s+/g, ""); - case "pascalCase": - return val - .replace(/([a-z])([A-Z])/g, "$1 $2") - .replace(/[_-]/g, " ") - .toLowerCase() - .replace(/(?:^\w|[A-Z]|\b\w)/g, (word) => word.toUpperCase()) - .replace(/\s+/g, ""); - case "kebabCase": - return val - .replace(/([a-z])([A-Z])/g, "$1 $2") - .replace(/[_-]/g, " ") - .toLowerCase() - .trim() - .replace(/\s+/g, "-"); - case "onlyNumbers": - return val.replace(/\D/g, ""); - case "onlyLetters": - return val.replace(/[^a-zA-Z]/g, ""); - case "onlyEmail": - return val.replace(/[^a-zA-Z0-9@._-]/g, ""); - case "onlyAlphanumeric": - return val.replace(/[^a-zA-Z0-9]/g, ""); - case "slugify": - return val - .normalize("NFD") - .replace(/[\u0300-\u036f]/g, "") - .toLowerCase() - .trim() - .replace(/[^a-z0-9\s_-]/g, "") - .replace(/[\s_-]+/g, "-") - .replace(/^-+|-+$/g, ""); - default: - return val; + const applyTransform = (val: string, t: string): string => { + switch (t) { + case 'toUpperCase': + return val.toUpperCase() + case 'toLowerCase': + return val.toLowerCase() + case 'capitalize': { + const trimmed = val.trim() + return ( + trimmed.charAt(0).toUpperCase() + + trimmed.slice(1).toLowerCase() + ) + } + case 'titleCase': + return val + .replace(/([a-z])([A-Z])/g, '$1 $2') + .replace(/[_-]/g, ' ') + .toLowerCase() + .trim() + .replace(/\s+/g, ' ') + .replace(/\b\w/g, (ch) => ch.toUpperCase()) + case 'snakeCase': + return val + .replace(/([a-z])([A-Z])/g, '$1 $2') + .replace(/[_-]/g, ' ') + .toLowerCase() + .trim() + .replace(/\s+/g, '_') + case 'camelCase': + return val + .replace(/([a-z])([A-Z])/g, '$1 $2') + .replace(/[_-]/g, ' ') + .toLowerCase() + .trim() + .replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) => + index === 0 ? word.toLowerCase() : word.toUpperCase(), + ) + .replace(/\s+/g, '') + case 'pascalCase': + return val + .replace(/([a-z])([A-Z])/g, '$1 $2') + .replace(/[_-]/g, ' ') + .toLowerCase() + .trim() + .replace(/(?:^\w|[A-Z]|\b\w)/g, (word) => + word.toUpperCase(), + ) + .replace(/\s+/g, '') + case 'kebabCase': + return val + .replace(/([a-z])([A-Z])/g, '$1 $2') + .replace(/[_-]/g, ' ') + .toLowerCase() + .trim() + .replace(/\s+/g, '-') + case 'onlyNumbers': + return val.replace(/\D/g, '') + case 'onlyLetters': + return val.replace(/[^a-zA-Z]/g, '') + case 'onlyEmail': + return val.replace(/[^a-zA-Z0-9@._-]/g, '') + case 'onlyAlphanumeric': + return val.replace(/[^a-zA-Z0-9]/g, '') + case 'slugify': + return val + .normalize('NFD') + .replace(/[\u0300-\u036f]/g, '') + .toLowerCase() + .trim() + .replace(/[^a-z0-9\s_-]/g, '') + .replace(/[\s_-]+/g, '-') + .replace(/^-+|-+$/g, '') + default: + return val + } } - }; - if (Array.isArray(transform)) { - return transform.reduce((acc, t) => applyTransform(acc, t), value); - } + if (Array.isArray(transform)) { + return transform.reduce((acc, t) => applyTransform(acc, t), value) + } - return transform ? applyTransform(value, transform) : value; -}; + return transform ? applyTransform(value, transform) : value +}