Newer
Older
percord / src / util / imports / OrmUtils.ts
// source: https://github.com/typeorm/typeorm/blob/master/src/util/OrmUtils.ts
// Copyright (c) 2015-2022 TypeORM. http://typeorm.github.io
/* eslint-disable @typescript-eslint/no-explicit-any */
// @fc-license-skip
export class OrmUtils {
    // Checks if it's an object made by Object.create(null), {} or new Object()
    private static isPlainObject(item: unknown) {
        if (item === null || item === undefined) {
            return false;
        }

        return !item.constructor || item.constructor === Object;
    }

    private static mergeArrayKey(target: any, key: number, value: any, memo: Map<any, any>) {
        // Have we seen this before?  Prevent infinite recursion.
        if (memo.has(value)) {
            target[key] = memo.get(value);
            return;
        }

        if (value instanceof Promise) {
            // Skip promises entirely.
            // This is a hold-over from the old code & is because we don't want to pull in
            // the lazy fields.  Ideally we'd remove these promises via another function first
            // but for now we have to do it here.
            return;
        }

        if (!this.isPlainObject(value) && !Array.isArray(value)) {
            target[key] = value;
            return;
        }

        if (!target[key]) {
            target[key] = Array.isArray(value) ? [] : {};
        }

        memo.set(value, target[key]);
        this.merge(target[key], value, memo);
        memo.delete(value);
    }

    private static mergeObjectKey(target: any, key: string, value: any, memo: Map<any, any>) {
        // Have we seen this before?  Prevent infinite recursion.
        if (memo.has(value)) {
            Object.assign(target, { [key]: memo.get(value) });
            return;
        }

        if (value instanceof Promise) {
            // Skip promises entirely.
            // This is a hold-over from the old code & is because we don't want to pull in
            // the lazy fields.  Ideally we'd remove these promises via another function first
            // but for now we have to do it here.
            return;
        }

        if (!this.isPlainObject(value) && !Array.isArray(value)) {
            Object.assign(target, { [key]: value });
            return;
        }

        if (!target[key]) {
            Object.assign(target, { [key]: value });
        }

        memo.set(value, target[key]);
        this.merge(target[key], value, memo);
        memo.delete(value);
    }

    private static merge(target: any, source: any, memo: Map<any, any> = new Map()): any {
        if (Array.isArray(target) && Array.isArray(source)) {
            for (let key = 0; key < source.length; key++) {
                this.mergeArrayKey(target, key, source[key], memo);
            }
        } else {
            for (const key of Object.keys(source)) {
                this.mergeObjectKey(target, key, source[key], memo);
            }
        }
    }

    /**
     * Deep Object.assign.
     */
    static mergeDeep(target: any, ...sources: any[]): any {
        if (!sources.length) {
            return target;
        }

        for (const source of sources) {
            OrmUtils.merge(target, source);
        }

        return target;
    }
}