From b513f61dd18aa79b58c278be03281e95b77d07b2 Mon Sep 17 00:00:00 2001 From: jfet97 Date: Sun, 6 Jan 2019 14:25:03 +0100 Subject: [PATCH] v 0.3.5 - WeakMap and WeakSet support --- README.md | 10 +++++----- __test__/omniclone.test.js | 26 ++++++++++++++++++++++++++ dist/main.js | 2 +- package.json | 2 +- src/deepclone.js | 12 ++++++++++++ src/omniclone.js | 6 +++++- 6 files changed, 50 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 5a0e403..8fbb41c 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ import omniclone from 'omniclone'; 2. let you to share the `[[Prototype]]` object between source and the resulting object (customizable behavior) 3. let you to clone objects with circular references (customizable behavior) 4. let you to copy getters and setters, non enumerables properties and also symbols (customizable behavior) -5. correct handling of String, Boolean, Number, Error and Promise objects +5. correct handling of String, Boolean, Number, Error, Promise, WeakMapm and WeakSet objects 6. safe similar sibilings references are not duplicated 7. correct cloning of Array objects (if the `invokeConstructors` flag is setted) 8. correct cloning of RegExp and Date objects @@ -168,7 +168,7 @@ omniclone(source, { }); ``` -## what about String, Boolean, Number, Error and Promise objects? +## what about String, Boolean, Number, Error, Promise, WeakMap and WeakSet objects? String, Boolean and Number objects passed to `omniclone` as sources will produce `null`.\ Error objects passed to `omniclone` as sources will produce `null` if the `discardErrorObjects` is set to `true` (as default).\ @@ -178,8 +178,8 @@ String, Boolean and Number objects props will be unwrapped.\ Error objects props will be discarded if the `discardErrorObjects` is set to `true` (as default).\ Error objects props will produce a `TypeError` if the `discardErrorObjects` is set to `false` (not the predefined behaviour). -Promise objects will be returned if passed to `omniclone` as sources.\ -Promise objects props will be shallow copied. +Promise, WeakMap and WeakSet objects will be returned if passed to `omniclone` as sources.\ +Promise, WeakMap and WeakSet objects props will be shallow copied. ## what about the 6th strength? @@ -201,4 +201,4 @@ When you will use `JSON.parse()`, an `{"foo":"bar"}` object will be created for 2. `super` is statically bound to a class heirarchy, remember it 3. `Error` objects cannot be properly copied because of js limitations 4. currently there is no isomorphic way to detect if an object is a `Proxy` nor is possible to access the handler object. Because of transparent virtualization, `omniclone` will copy each properties, the `constructor` and the `[[Prototype]]` directly from the proxed object. -5. currenlty there is a lack of support for Map, Set, WeakMap and WeakSet objects. I'm working on this. +5. currenlty there is a lack of support for Map and Set objects. I'm working on this. diff --git a/__test__/omniclone.test.js b/__test__/omniclone.test.js index 9de3798..19dc887 100644 --- a/__test__/omniclone.test.js +++ b/__test__/omniclone.test.js @@ -666,6 +666,18 @@ describe("omniclone", () => { expect(res).toBe(p); }); + it("should return the weakmap when a WeakMap object is passed as source", () => { + const wm = new WeakMap(); + const res = omniclone(wm); + expect(res).toBe(wm); + }); + + it("should return the weakset when a WeakSet object is passed as source", () => { + const ws = new WeakSet(); + const res = omniclone(ws); + expect(res).toBe(ws); + }); + it("should shallow copy a Promise prop", () => { const p = Promise.resolve(); const ob1 = { p }; @@ -673,6 +685,20 @@ describe("omniclone", () => { expect(res.p).toBe(ob1.p); }); + it("should shallow copy a WeakMap prop", () => { + const wm = new WeakMap(); + const ob1 = { wm }; + const res = omniclone(ob1); + expect(res.vm).toBe(ob1.vm); + }); + + it("should shallow copy a WeakSet prop", () => { + const ws = new WeakSet(); + const ob1 = { ws }; + const res = omniclone(ob1); + expect(res.ws).toBe(ob1.ws); + }); + it("should clone a RegExp if it is passed as source", () => { (() => { const r = new RegExp("foo", "g"); diff --git a/dist/main.js b/dist/main.js index 07034a1..b2d7f5e 100644 --- a/dist/main.js +++ b/dist/main.js @@ -1 +1 @@ -module.exports=function(e){var r={};function t(o){if(r[o])return r[o].exports;var n=r[o]={i:o,l:!1,exports:{}};return e[o].call(n.exports,n,n.exports,t),n.l=!0,n.exports}return t.m=e,t.c=r,t.d=function(e,r,o){t.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:o})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,r){if(1&r&&(e=t(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(t.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)t.d(o,n,function(r){return e[r]}.bind(null,n));return o},t.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(r,"a",r),r},t.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},t.p="",t(t.s=0)}([function(e,r,t){const o=t(1);e.exports=function(e={},{setPrototype:r=!1,invokeConstructors:t=!0,copyNonEnumerables:n=!1,copySymbols:i=!1,copyGettersSetters:c=!1,allowCircularReferences:s=!1,discardErrorObjects:a=!0}={}){if(!e||"object"!=typeof e)throw new TypeError("TypeError: invalid 'obj' argument's type");if(e instanceof Number||e instanceof String||e instanceof Boolean)return null;if(e instanceof Promise)return e;if(e instanceof Error){if(a)return null;throw new TypeError("TypeError: cannot copy Error objects")}if(e instanceof RegExp){const{source:r,flags:t,lastIndex:o}=e,n=new RegExp(r,t);return n.lastIndex=o,n}if(e instanceof Date)return new Date(e.getTime());if("boolean"!=typeof r)throw new TypeError("TypeError: invalid 'setPrototype' flag's type");if("boolean"!=typeof t)throw new TypeError("TypeError: invalid 'invokeConstructors' flag's type");if("boolean"!=typeof n)throw new TypeError("TypeError: invalid 'copyNonEnumerables' flag's type");if("boolean"!=typeof i)throw new TypeError("TypeError: invalid 'copySymbols' flag's type");if("boolean"!=typeof c)throw new TypeError("TypeError: invalid 'copyGettersSetters' flag's type");if("boolean"!=typeof s)throw new TypeError("TypeError: invalid 'allowCircularReferences' flag's type");if("boolean"!=typeof a)throw new TypeError("TypeError: invalid 'discardErrorObjects' flag's type");return o(e,{setPrototype:r,invokeConstructors:t,copyNonEnumerables:n,copySymbols:i,copyGettersSetters:c,allowCircularReferences:s,discardErrorObjects:a})}},function(e,r){e.exports=function(e,r){const t=new WeakMap,o=e;function n(e,r,t,n){return function(e,r,t,n,c){const{copyNonEnumerables:s,copySymbols:a,copyGettersSetters:f,allowCircularReferences:l,discardErrorObjects:u}=t;Object.entries(r).forEach(([r,p])=>{const{value:y,enumerable:b}=p;if((s||b)&&(a||"symbol"!=typeof y)&&(f||!p.get&&!p.set))if(y&&"object"==typeof y){if(n.has(y))return void(e[r]=n.get(y));if(c.has(y)){if(l)return void(e[r]=c.get(y));throw new TypeError("TypeError: circular reference found")}if(y instanceof Error){if(u)return;throw new TypeError("TypeError: cannot copy Error objects")}if(y instanceof Number||y instanceof Boolean){const t=p.value.valueOf();return void Object.defineProperty(e,r,{...p,...{value:t}})}if(y instanceof String){const t=p.value.toString();return void Object.defineProperty(e,r,{...p,...{value:t}})}if(y instanceof Date){const t=new Date(p.value.getTime());return void Object.defineProperty(e,r,{...p,...{value:t}})}if(y instanceof RegExp){const{value:{lastIndex:t}}=p,o=new RegExp(p.value.source,p.value.flags);return Object.defineProperty(e,r,{...p,...{value:o}}),void(e[r].lastIndex=t)}if(y instanceof Promise)return void Object.defineProperty(e,r,p);e[r]=i(y,t,c,o),n.set(y,e[r])}else{const t=Object.getOwnPropertyDescriptor(e,r);t&&!t.configurable||Object.defineProperty(e,r,p)}})}(e,r,t,new WeakMap,n)}function i(e,r,t,o){const{setPrototype:i,invokeConstructors:c,allowCircularReferences:s}=r;t.set(e,e);let a=null;const f=Object.getOwnPropertyDescriptors(e);return n(a=c?new e.constructor:i?Object.create(Object.getPrototypeOf(e)):{},f,r,t),s&&(t.set(e,a),o===e&&function(e,r){const t=new WeakMap;t.set(e),function e(r,t,o){Object.entries(r).forEach(([n,i])=>{if(i&&"object"==typeof i)if(t.has(i))r[n]=t.get(i);else{if(o.has(i))return;o.set(i),e(i,t,o)}})}(e,r,t)}(a,t)),a}return i(e,r,t,o)}}]); \ No newline at end of file +module.exports=function(e){var r={};function t(o){if(r[o])return r[o].exports;var n=r[o]={i:o,l:!1,exports:{}};return e[o].call(n.exports,n,n.exports,t),n.l=!0,n.exports}return t.m=e,t.c=r,t.d=function(e,r,o){t.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:o})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,r){if(1&r&&(e=t(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(t.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)t.d(o,n,function(r){return e[r]}.bind(null,n));return o},t.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(r,"a",r),r},t.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},t.p="",t(t.s=0)}([function(e,r,t){const o=t(1);e.exports=function(e={},{setPrototype:r=!1,invokeConstructors:t=!0,copyNonEnumerables:n=!1,copySymbols:i=!1,copyGettersSetters:c=!1,allowCircularReferences:a=!1,discardErrorObjects:s=!0}={}){if(!e||"object"!=typeof e)throw new TypeError("TypeError: invalid 'obj' argument's type");if(e instanceof Number||e instanceof String||e instanceof Boolean)return null;if(e instanceof Promise||e instanceof WeakMap||e instanceof WeakSet)return e;if(e instanceof Error){if(s)return null;throw new TypeError("TypeError: cannot copy Error objects")}if(e instanceof RegExp){const{source:r,flags:t,lastIndex:o}=e,n=new RegExp(r,t);return n.lastIndex=o,n}if(e instanceof Date)return new Date(e.getTime());if("boolean"!=typeof r)throw new TypeError("TypeError: invalid 'setPrototype' flag's type");if("boolean"!=typeof t)throw new TypeError("TypeError: invalid 'invokeConstructors' flag's type");if("boolean"!=typeof n)throw new TypeError("TypeError: invalid 'copyNonEnumerables' flag's type");if("boolean"!=typeof i)throw new TypeError("TypeError: invalid 'copySymbols' flag's type");if("boolean"!=typeof c)throw new TypeError("TypeError: invalid 'copyGettersSetters' flag's type");if("boolean"!=typeof a)throw new TypeError("TypeError: invalid 'allowCircularReferences' flag's type");if("boolean"!=typeof s)throw new TypeError("TypeError: invalid 'discardErrorObjects' flag's type");return o(e,{setPrototype:r,invokeConstructors:t,copyNonEnumerables:n,copySymbols:i,copyGettersSetters:c,allowCircularReferences:a,discardErrorObjects:s})}},function(e,r){e.exports=function(e,r){const t=new WeakMap,o=e;function n(e,r,t,n){return function(e,r,t,n,c){const{copyNonEnumerables:a,copySymbols:s,copyGettersSetters:f,allowCircularReferences:l,discardErrorObjects:u}=t;Object.entries(r).forEach(([r,p])=>{const{value:y,enumerable:b}=p;if((a||b)&&(s||"symbol"!=typeof y)&&(f||!p.get&&!p.set))if(y&&"object"==typeof y){if(n.has(y))return void(e[r]=n.get(y));if(c.has(y)){if(l)return void(e[r]=c.get(y));throw new TypeError("TypeError: circular reference found")}if(y instanceof Error){if(u)return;throw new TypeError("TypeError: cannot copy Error objects")}if(y instanceof Number||y instanceof Boolean){const t=p.value.valueOf();return void Object.defineProperty(e,r,{...p,...{value:t}})}if(y instanceof String){const t=p.value.toString();return void Object.defineProperty(e,r,{...p,...{value:t}})}if(y instanceof Date){const t=new Date(p.value.getTime());return void Object.defineProperty(e,r,{...p,...{value:t}})}if(y instanceof RegExp){const{value:{lastIndex:t}}=p,o=new RegExp(p.value.source,p.value.flags);return Object.defineProperty(e,r,{...p,...{value:o}}),void(e[r].lastIndex=t)}if(y instanceof Promise)return void Object.defineProperty(e,r,p);if(y instanceof WeakMap)return void Object.defineProperty(e,r,p);if(y instanceof WeakSet)return void Object.defineProperty(e,r,p);e[r]=i(y,t,c,o),n.set(y,e[r])}else{const t=Object.getOwnPropertyDescriptor(e,r);t&&!t.configurable||Object.defineProperty(e,r,p)}})}(e,r,t,new WeakMap,n)}function i(e,r,t,o){const{setPrototype:i,invokeConstructors:c,allowCircularReferences:a}=r;t.set(e,e);let s=null;const f=Object.getOwnPropertyDescriptors(e);return n(s=c?new e.constructor:i?Object.create(Object.getPrototypeOf(e)):{},f,r,t),a&&(t.set(e,s),o===e&&function(e,r){const t=new WeakMap;t.set(e),function e(r,t,o){Object.entries(r).forEach(([n,i])=>{if(i&&"object"==typeof i)if(t.has(i))r[n]=t.get(i);else{if(o.has(i))return;o.set(i),e(i,t,o)}})}(e,r,t)}(s,t)),s}return i(e,r,t,o)}}]); \ No newline at end of file diff --git a/package.json b/package.json index 8bda1ed..d2ffdbb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "omniclone", - "version": "0.3.4", + "version": "0.3.5", "description": "deep cloning function for js objects", "main": "dist/main.js", "scripts": { diff --git a/src/deepclone.js b/src/deepclone.js index 3f29293..ac5b748 100644 --- a/src/deepclone.js +++ b/src/deepclone.js @@ -143,6 +143,18 @@ function deepClone(source, config) { return; } + // WeakMaps are shallow cloned + if (value instanceof WeakMap) { + Object.defineProperty(res, prop, descriptor); + return; + } + + // WeakSets are shallow cloned + if (value instanceof WeakSet) { + Object.defineProperty(res, prop, descriptor); + return; + } + // recursive deep copy for the others object props // eslint-disable-next-line no-use-before-define res[prop] = innerDeepClone(value, config, references, start); diff --git a/src/omniclone.js b/src/omniclone.js index 9a63c72..f64e769 100644 --- a/src/omniclone.js +++ b/src/omniclone.js @@ -24,7 +24,11 @@ function omniclone( return null; } - if (obj instanceof Promise) { + if ( + obj instanceof Promise || + obj instanceof WeakMap || + obj instanceof WeakSet + ) { return obj; }