export const toCamelCase = (str: string) => str.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()) type P = (data: I) => O type PipeFns = { (a: P): A (a: P, b: P): B (a: P, b: P, c: P): C (a: P, b: P, c: P, d: P): D (a: P, b: P, c: P, d: P, e: P): E (a: P, b: P, c: P, d: P, e: P, f: P): F (a: P, b: P, c: P, d: P, e: P, f: P, g: P): G (a: P, b: P, c: P, d: P, e: P, f: P, g: P, h: P): H (...fns: Array>): T } export const pipe = (data: T) => ((...fns: Array) => fns.reduce((acc, fn) => fn(acc), data)) as PipeFns export const isNumber = (data: any) => { if (typeof data === 'number') { return true } if (typeof data === 'string' && data !== '') { return !isNaN(Number(data)) } return false } export const smartSplit = (str: string, separator = ' ' as string | RegExp) => { const escaper = '&&&' return pipe(str)( x => x.replace(/\s\?\?\s/g, `${escaper}??${escaper}`), x => x.replace(/\s([+\-*/])\s/g, `${escaper}$1${escaper}`), x => x.split(separator), x => x.map(token => token.replace(new RegExp(escaper, 'g'), ' ')), ) } export const addMissingSpaces = (str: string) => pipe(str)( x => x.trim(), x => x.replace(/\](?=\d)/g, '] '), x => x.replace(/\](?=")/g, '] '), x => x.replace(/\)(?=[^\s,])/g, ') '), x => x.replace(/(? { try { new Function(`const test = ${jsValueString}`) return true } catch { return false } } export const shouldBeSerialized = (value: string) => { if (value.includes('-')) { return value.split('-').some(shouldBeSerialized) } return [ isNumber(value), value.startsWith('vars['), value.startsWith('rt.'), /[*/+-]/.test(value), value.includes('"'), value.includes(' '), value === '(', value === ')', ].some(Boolean) } export const roundToPrecision = (value: number, precision: number) => parseFloat(value.toFixed(precision)) export const deepEqual = (a: T, b: T): boolean => { if (Object.is(a, b)) { return true } if ( typeof a !== 'object' || a === null || typeof b !== 'object' || b === null ) { return false } const keysA = Object.keys(a) as Array if (keysA.length !== Object.keys(b).length) { return false } return keysA.every(key => deepEqual(a[key], b[key]) && Object.prototype.hasOwnProperty.call(b, key)) } export const removeKeys = , TKey extends keyof TObj>(obj: TObj, keysToRemove: Array) => Object.fromEntries( Object.entries(obj).filter(([key]) => !keysToRemove.includes(key as TKey)), ) as Omit