perf: add and use homemade optimized deep cloning implementation
authorJérôme Benoit <jerome.benoit@sap.com>
Wed, 6 Sep 2023 23:46:41 +0000 (01:46 +0200)
committerJérôme Benoit <jerome.benoit@sap.com>
Wed, 6 Sep 2023 23:46:41 +0000 (01:46 +0200)
Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
package.json
pnpm-lock.yaml
src/utils/Utils.ts

index 460bb2139c9ed67201257274830711042133de56..e1732833089f5b5fecfb3aeb10416e9442455a82 100644 (file)
     "basic-ftp": "^5.0.3",
     "chalk": "^5.3.0",
     "date-fns": "^2.30.0",
-    "deep-clone": "^4.0.0",
     "http-status-codes": "^2.2.0",
     "just-merge": "^3.2.0",
     "logform": "^2.5.1",
index 8578238537010356b65680a51976f53ef0e27695..c685cfe35093f04687e151be9e228dc79d933c4d 100644 (file)
@@ -40,9 +40,6 @@ dependencies:
   date-fns:
     specifier: ^2.30.0
     version: 2.30.0
-  deep-clone:
-    specifier: ^4.0.0
-    version: 4.0.0
   http-status-codes:
     specifier: ^2.2.0
     version: 2.2.0
@@ -3452,10 +3449,6 @@ packages:
       mimic-response: 3.1.0
     dev: true
 
-  /deep-clone@4.0.0:
-    resolution: {integrity: sha512-bMvDVR8GiGCGHT4SgqXyXDD9Zmo3kv9YLq8aSO2xslP97A3mFkpNBg+t+fjXERvewzhmtk9efvL+V52iVkD0lg==}
-    dev: false
-
   /deep-extend@0.6.0:
     resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
     engines: {node: '>=4.0.0'}
index e01aa75f34cdec07237f3cbe5c1f60bd98cabfd9..c29f8d156687139aeefb640ae5e0741add8c2924 100644 (file)
@@ -12,7 +12,6 @@ import {
   minutesToSeconds,
   secondsToMilliseconds,
 } from 'date-fns';
-import deepClone from 'deep-clone';
 
 import { Constants } from './Constants';
 import { type TimestampedData, WebSocketCloseEventStatusString } from '../types';
@@ -215,8 +214,44 @@ type CloneableData =
   | CloneableData[]
   | { [key: string]: CloneableData };
 
+type FormatKey = (key: string) => string;
+
+function deepClone<I extends CloneableData, O extends CloneableData = I>(
+  value: I,
+  formatKey?: FormatKey,
+  refs: Map<I, O> = new Map<I, O>(),
+): O {
+  const ref = refs.get(value);
+  if (ref !== undefined) {
+    return ref;
+  }
+  if (Array.isArray(value)) {
+    const clone: CloneableData[] = [];
+    refs.set(value, clone as O);
+    for (let i = 0; i < value.length; i++) {
+      clone[i] = deepClone(value[i], formatKey, refs);
+    }
+    return clone as O;
+  }
+  if (value instanceof Date) {
+    return new Date(value.valueOf()) as O;
+  }
+  if (typeof value !== 'object' || value === null) {
+    return value as unknown as O;
+  }
+  const clone: Record<string, CloneableData> = {};
+  refs.set(value, clone as O);
+  for (const key of Object.keys(value)) {
+    clone[typeof formatKey === 'function' ? formatKey(key) : key] = deepClone(
+      value[key],
+      formatKey,
+      refs,
+    );
+  }
+  return clone as O;
+}
+
 export const cloneObject = <T>(object: T): T => {
-  // eslint-disable-next-line @typescript-eslint/no-unsafe-call
   return deepClone(object as CloneableData) as T;
 };