const ssxElement = Symbol.for("ssx.element"); const objectConstructor = {}.constructor; /** TypeScript helper to create optional properties recursively */ export type DeepPartial = T extends object ? { [P in keyof T]?: DeepPartial; } : T; /** Check if the argument passed is a plain object */ export function isPlainObject(obj: unknown): obj is Record { return typeof obj === "object" && obj !== null && (obj.constructor === objectConstructor || obj.constructor === undefined) && // @ts-ignore: Check if the argument passed is a SSX element obj[ssxElement] !== true && // @ts-ignore: Check if the argument passed is a Page.data object obj !== obj.page?.data; } /** * Merge two objects recursively. * It's used to merge user options with default options. */ export function merge( defaults: Type, user?: Type, ): Required { const merged = { ...defaults }; if (!user) { return merged as unknown as Required; } for (const [key, value] of Object.entries(user)) { if (value === undefined) { continue; } // @ts-ignore: No index signature with a parameter of type 'string' was found on type 'unknown' if (isPlainObject(merged[key]) && isPlainObject(value)) { // @ts-ignore: Type 'string' cannot be used to index type 'Type' merged[key] = merge(merged[key], value); continue; } // @ts-ignore: Type 'string' cannot be used to index type 'Type' merged[key] = value; } return merged as unknown as Required; }