@lodestar/utils
Version:
Utilities required across multiple lodestar packages
103 lines (88 loc) • 3.06 kB
text/typescript
import Case from "case";
export type KeyCase =
| "snake"
| "constant"
| "camel"
| "param"
| "header"
| "pascal" //Same as squish
| "dot"
| "notransform";
export function toExpectedCase(
value: string,
expectedCase: KeyCase = "camel",
customCasingMap?: Record<string, string>
): string {
if (expectedCase === "notransform") return value;
if (customCasingMap?.[value]) return customCasingMap[value];
switch (expectedCase) {
case "param":
return Case.kebab(value);
case "dot":
return Case.lower(value, ".", true);
default:
return Case[expectedCase](value);
}
}
function isObjectObject(val: unknown): val is object {
return val != null && typeof val === "object" && Array.isArray(val) === false;
}
export function isPlainObject(o: unknown): o is object {
if (isObjectObject(o) === false) return false;
// If has modified constructor
const ctor = (o as Record<string, unknown>).constructor;
if (typeof ctor !== "function") return false;
// If has modified prototype
const prot = ctor.prototype;
if (isObjectObject(prot) === false) return false;
// If constructor does not have an Object-specific method
if (Object.prototype.hasOwnProperty.call(prot, "isPrototypeOf") === false) {
return false;
}
// Most likely a plain Object
return true;
}
export function isEmptyObject(value: unknown): boolean {
return isObjectObject(value) && Object.keys(value).length === 0;
}
/**
* Creates an object with the same keys as object and values generated by running each own enumerable
* string keyed property of object thru iteratee.
*
* Inspired on lodash.mapValues, see https://lodash.com/docs/4.17.15#mapValues
*/
// biome-ignore lint/suspicious/noExplicitAny: We need to use `any` type here
export function mapValues<T extends {[K: string]: any}, R>(
obj: T,
iteratee: (value: T[keyof T], key: keyof T) => R
): {[K in keyof T]: R} {
const output = {} as {[K in keyof T]: R};
for (const [key, value] of Object.entries(obj)) {
output[key as keyof T] = iteratee(value, key);
}
return output;
}
export function objectToExpectedCase<T extends Record<string, unknown> | Record<string, unknown>[] | unknown[]>(
obj: T,
expectedCase: "snake" | "constant" | "camel" | "param" | "header" | "pascal" | "dot" | "notransform" = "camel"
): T {
if (Array.isArray(obj)) {
const newArr: unknown[] = [];
for (let i = 0; i < obj.length; i++) {
newArr[i] = objectToExpectedCase(obj[i] as T, expectedCase);
}
return newArr as unknown as T;
}
if (Object(obj) === obj) {
const newObj: Record<string, unknown> = {};
for (const name of Object.getOwnPropertyNames(obj)) {
const newName = toExpectedCase(name, expectedCase);
if (newName !== name && Object.prototype.hasOwnProperty.call(obj, newName)) {
throw new Error(`object already has a ${newName} property`);
}
newObj[newName] = objectToExpectedCase(obj[name] as Record<string, unknown>, expectedCase);
}
return newObj as T;
}
return obj;
}