From e5b173155a78729e58fc33e14f302c554f9035c0 Mon Sep 17 00:00:00 2001 From: Emma Date: Tue, 20 May 2025 10:22:49 -0600 Subject: [PATCH] feat: change evaluation now adds case transformation for capture groups --- tools/fieldRename.ts | 40 ++++++++++++++++++++++++++++----- util/caseManagement.ts | 50 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 80 insertions(+), 10 deletions(-) diff --git a/tools/fieldRename.ts b/tools/fieldRename.ts index e99b4e9..9fb6868 100644 --- a/tools/fieldRename.ts +++ b/tools/fieldRename.ts @@ -7,6 +7,7 @@ import { colorize } from "../cli/style.ts"; import { cliAlert, cliLog, cliPrompt } from "../cli/prompts.ts"; import { multiSelectMenuInteractive } from "../cli/selectMenu.ts"; import type { callback, ITool } from "../types.ts"; +import { toCase } from "util/caseManagement.ts"; async function renameFields( path: string, @@ -69,13 +70,42 @@ function applyRename( } } +/*** + * Evaluates the change string with the match array + * + * @description The change string can include the following variables: + * + * - $ - capture groups, indexed from 1 + * - $i - capture groups, indexed from 1, transforming an integer to an index + * - $s - capture groups, indexed from 1, transforming a string to snake case + * - $c - capture groups, indexed from 1, transforming a string to camel case + * - $l - capture groups, indexed from 1, transforming a string to lower case + * - $u - capture groups, indexed from 1, transforming a string to upper case + * - $t - capture groups, indexed from 1, transforming a string to title case + */ function evaluateChange(change: string, match: RegExpExecArray) { return change.replace( - /\$(\d+)(i?)/g, - (_, i, indexed) => - indexed - ? (parseInt(match[i]) ? (parseInt(match[i]) - 1).toString() : match[i]) - : match[i], + /\$(\d+)([icslut]?)/g, + (_, i, indexed) => { + switch (indexed) { + case "i": + return (parseInt(match[i]) + ? (parseInt(match[i]) - 1).toString() + : match[i]); + case "s": + return toCase(match[i], "snake"); + case "c": + return toCase(match[i], "camel"); + case "t": + return toCase(match[i], "title"); + case "l": + return match[i].toLowerCase(); + case "u": + return match[i].toUpperCase(); + default: + return match[i]; + } + }, ); } diff --git a/util/caseManagement.ts b/util/caseManagement.ts index d538c68..b5a4566 100644 --- a/util/caseManagement.ts +++ b/util/caseManagement.ts @@ -11,9 +11,45 @@ function lowerToTrainCase(str: string) { ); } -function lowerToCamelCase(str: string) { - return str.trim().replace(/(?:\s)\w/g, (match) => match.toUpperCase()) - .replaceAll(" ", ""); +/** + * @param str + * @returns camelCased string (single letter words are lower cased, e.g. SSN -> ssn) + */ +function lowerToCamelCase(str: string): string { + const words = str.trim().split(/\s+/); + const result: string[] = []; + let i = 0; + + while (i < words.length) { + if (words[i].length === 1) { + // We’ve hit the start of a chain of single-letter words + let j = i; + while (j < words.length && words[j].length === 1) { + j++; + } + const chainIsAtStart = i === 0; + // Process that entire chain + for (let k = i; k < j; k++) { + result[k] = chainIsAtStart + ? words[k].toLowerCase() + : words[k].toUpperCase(); + } + i = j; + } else { + // Normal multi-letter word + if (i === 0) { + // first word: all lower + result[i] = words[i].toLowerCase(); + } else { + // subsequent words: capitalize first letter + result[i] = words[i][0].toUpperCase() + + words[i].slice(1).toLowerCase(); + } + i++; + } + } + + return result.join(""); } function lowerToSnakeCase(str: string) { @@ -88,10 +124,10 @@ function coerceCaseToLower(str: string, caseType: CaseType) { case "macro": case "snake": case "upper": - return str.replace("_", " ").toLowerCase(); + return str.replaceAll("_", " ").toLowerCase(); case "train": case "kebab": - return str.replace("-", " ").toLowerCase(); + return str.replaceAll("-", " ").toLowerCase(); default: return str.toLowerCase(); } @@ -124,3 +160,7 @@ export function toCase(str: string, toCase: CaseType) { return str; } } + +if (import.meta.main) { + console.log(toCase("SSN", "camel")); +}