typedash
Version:
modern, type-safe collection of utility functions
1 lines • 2.97 kB
Source Map (JSON)
{"version":3,"file":"startCase-DMTQunoR.cjs","names":["capitalize"],"sources":["../src/functions/startCase/startCase.ts"],"sourcesContent":["import { capitalize } from '../capitalize';\n\n/**\n * Converts a string to start case (first letter of each word capitalized, joined by spaces).\n * @param string The input string to convert to start case.\n * @returns A new string converted to start case.\n * @example\n * ```ts\n * startCase('--foo-bar--') // 'Foo Bar'\n * startCase('fooBar') // 'Foo Bar'\n * startCase('__FOO_BAR__') // 'FOO BAR'\n * ```\n */\nexport function startCase<S extends string>(string: S): StartCase<S> {\n if (!/[a-z]+/i.test(string)) {\n return string as unknown as StartCase<S>;\n }\n\n return string\n .match(WORDS_REGEX)\n ?.map((word) => capitalize(word))\n .join(' ') as StartCase<S>;\n}\n\nconst WORDS_REGEX =\n /[A-Z]{2,}(?=[A-Z][a-z]+\\d*|\\b)|[A-Z]?[a-z]+\\d*|[A-Z]+|\\d+/g;\n\n// Type-level helper: true for A-Z, false for digits/lowercase/special\ntype IsUpperLetter<C extends string> =\n C extends Uppercase<C> ? (C extends Lowercase<C> ? false : true) : false;\n\n// Trim trailing spaces (from trailing separators like '--foo--')\ntype TrimTrailingSpaces<S extends string> = S extends `${infer L} `\n ? TrimTrailingSpaces<L>\n : S;\n\n// Core recursive type: processes char-by-char with state\ntype StartCaseImpl<\n S extends string,\n PrevIsUpper extends boolean = false,\n AtWordStart extends boolean = true,\n> = S extends `${infer C}${infer Rest}`\n ? C extends '-' | '_' | ' '\n ? // Separator: emit space (unless at start), mark next as word start\n `${AtWordStart extends true ? '' : ' '}${StartCaseImpl<Rest, false, true>}`\n : AtWordStart extends true\n ? // Word start: capitalize\n `${Uppercase<C>}${StartCaseImpl<Rest, IsUpperLetter<C>, false>}`\n : IsUpperLetter<C> extends true\n ? PrevIsUpper extends true\n ? // Consecutive uppercase: peek ahead to detect acronym end (FOOBar → FOO|Bar)\n Rest extends `${infer Next}${string}`\n ? Next extends '-' | '_' | ' '\n ? `${C}${StartCaseImpl<Rest, true, false>}`\n : IsUpperLetter<Next> extends true\n ? `${C}${StartCaseImpl<Rest, true, false>}`\n : ` ${C}${StartCaseImpl<Rest, true, false>}`\n : `${C}${StartCaseImpl<Rest, true, false>}`\n : // Uppercase after lowercase: new word\n ` ${C}${StartCaseImpl<Rest, true, false>}`\n : // Lowercase/digit: continue word\n `${C}${StartCaseImpl<Rest, false, false>}`\n : '';\n\n/**\n * Converts a string to start case at the type level.\n * @see {@link startCase}.\n */\nexport type StartCase<S extends string> = TrimTrailingSpaces<StartCaseImpl<S>>;\n"],"mappings":";;;;;;;;;;;;;;AAaA,SAAgB,UAA4B,QAAyB;AACnE,KAAI,CAAC,UAAU,KAAK,OAAO,CACzB,QAAO;AAGT,QAAO,OACJ,MAAM,YAAY,EACjB,KAAK,SAASA,8BAAW,KAAK,CAAC,CAChC,KAAK,IAAI;;AAGd,MAAM,cACJ"}