Mirror of https://github.com/roostorg/coop
github.com/roostorg/coop
1import { JsonValue, type Opaque } from 'type-fest';
2
3// Split by newline and by commas, see https://stackoverflow.com/a/34316181
4export function splitByWhitespaceAndCommas(input: string): string[] {
5 return input.trim().split(/[\s,]+/);
6}
7
8function ucFirst(s: string): string {
9 return (s[0] ?? '').toUpperCase() + s.slice(1);
10}
11
12/**
13 * If the string s is formatted like an 'ENUM_VALUE',
14 * then change it to 'Enum Value'.
15 */
16export function titleCaseEnumString(s: string): string {
17 return s
18 .split('_')
19 .map((word) => word.toLowerCase())
20 .map(ucFirst)
21 .join(' ');
22}
23
24export function titleCaseEnumStringWithArticle(s: string): string {
25 const titleCased = titleCaseEnumString(s);
26 return ['A', 'a', 'E', 'e', 'I', 'i', 'O', 'o', 'U', 'u'].includes(
27 titleCased[0],
28 )
29 ? `an ${titleCased}`
30 : `a ${titleCased}`;
31}
32
33export function prettyPrintJson(it: string) {
34 return prettyPrintJsonValue(JSON.parse(it));
35}
36
37export function prettyPrintJsonValue(it: JsonValue) {
38 return JSON.stringify(it, undefined, 4);
39}
40
41export function isValidJson(it: string) {
42 try {
43 JSON.parse(it);
44 return true;
45 } catch (e) {
46 return false;
47 }
48}
49
50/**
51 * Converts a string to a human readable label with a 'best effort' approach,
52 * specifically looking for snake case, camel case, and spaces. Full disclosure,
53 * this was written by ChatGPT.
54 */
55export function toHumanReadableLabel(input: string): string {
56 // Define regular expressions for different cases
57 const camelCase = /([a-z])([A-Z])/g;
58 const snakeCase = /_/g;
59 const spaceCase = / /g;
60 const kebabCase = /^[a-z]+(-[a-z]+)*$/;
61 const pascalCase = /^[A-Z][a-zA-Z]*$/;
62
63 if (camelCase.test(input)) {
64 return input
65 .replace(camelCase, '$1 $2')
66 .split(' ')
67 .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
68 .join(' ');
69 } else if (snakeCase.test(input) || spaceCase.test(input)) {
70 return input
71 .replace(snakeCase, ' ')
72 .split(' ')
73 .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
74 .join(' ');
75 } else if (kebabCase.test(input)) {
76 return input
77 .split('-')
78 .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
79 .join(' ');
80 } else if (pascalCase.test(input)) {
81 return input.charAt(0).toUpperCase() + input.slice(1);
82 }
83
84 // If not any of the above cases, just return the original string with first character capitalized
85 return input.charAt(0).toUpperCase() + input.slice(1);
86}
87
88export function truncateIdIfNeeded(id: string | undefined, maxLength: number) {
89 return id && id.length > maxLength ? id.substring(0, maxLength) + '...' : id;
90}
91
92export type NonEmptyString = Opaque<string, 'NonEmptyString'>;
93
94export function isNonEmptyString(it: unknown): it is NonEmptyString {
95 return typeof it === 'string' && it !== '';
96}