Skip to content

Commit

Permalink
Merge pull request #4 from jfet97/fix-algo
Browse files Browse the repository at this point in the history
7.0.0
  • Loading branch information
jfet97 committed Jan 8, 2019
2 parents 2ad1ea0 + 797fa0a commit 0aee706
Show file tree
Hide file tree
Showing 12 changed files with 55 additions and 107 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import omniclone from 'omniclone';
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, Promise, Map, Set, WeakMap and WeakSet objects
6. safe similar sibilings references are not duplicated
6. similar references are not duplicated
7. correct cloning of Array objects
8. correct cloning of RegExp and Date objects

Expand Down Expand Up @@ -151,9 +151,10 @@ const res = omniclone(source, {
```
Odds are that to properly copy gets&setts you have also to enable the `copyNonEnumerables` flag.

### allowCircularReferences (default false)
### allowCircularReferences (default true)
Enable it to allow circular references.\
Disable it to throw an error if one is met.
Know that `omniclone` is more performing with this flag __enabled__, so disable it only if you really need.
```js
const res = omniclone(source, {
allowCircularReferences: true
Expand All @@ -179,7 +180,7 @@ omniclone(source, {
copyNonEnumerables : false,
copySymbols : false,
copyGettersSetters : false,
allowCircularReferences: false,
allowCircularReferences: true,
discardErrorObjects: true,
});
```
Expand Down
25 changes: 10 additions & 15 deletions __test__/omniclone.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ describe("omniclone", () => {

it(`should set setPrototype to false, invokeConstructors to true, discardErrorObjects to true,
copyNonEnumerables to false, copySymbols to false, copyGettersSetters to false and
allowCircularReferences to false if the config argument is undefined`, () => {
allowCircularReferences to true if the config argument is undefined`, () => {
(() => {
let i = 0;
class Test {
Expand Down Expand Up @@ -48,7 +48,7 @@ describe("omniclone", () => {
const ob1 = {};
ob1.ob1 = ob1;
omniclone(ob1);
}).toThrow(TypeError("TypeError: circular reference found"));
}).not.toThrow(TypeError("TypeError: circular reference found"));

(() => {
class MyError extends Error {}
Expand Down Expand Up @@ -544,16 +544,7 @@ describe("omniclone", () => {
const ob1 = {};
ob1.ob1 = ob1;
expect(() => {
omniclone(ob1);
}).toThrow(TypeError("TypeError: circular reference found"));
})();

(() => {
const ob1 = {};
const ob2 = { ob1 };
ob1.ob2 = ob2;
expect(() => {
omniclone(ob1);
omniclone(ob1, { allowCircularReferences: false });
}).toThrow(TypeError("TypeError: circular reference found"));
})();
});
Expand All @@ -572,7 +563,7 @@ describe("omniclone", () => {
const ob2 = { ob1 };
ob1.ob2 = ob2;
expect(() => {
omniclone(ob1, { allowCircularReferences: true });
omniclone(ob1);
}).toBeDefined();
})();

Expand Down Expand Up @@ -1037,7 +1028,9 @@ describe("omniclone", () => {
const map = new Map();
map.set(map, map);
expect(() => {
omniclone(map);
omniclone(map, {
allowCircularReferences: false
});
}).toThrow(TypeError("TypeError: circular reference found"));
})();

Expand Down Expand Up @@ -1335,7 +1328,9 @@ describe("omniclone", () => {
const set = new Set();
set.add(set);
expect(() => {
omniclone(set);
omniclone(set, {
allowCircularReferences: false
});
}).toThrow(TypeError("TypeError: circular reference found"));
})();

Expand Down
2 changes: 1 addition & 1 deletion dist/main.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "omniclone",
"version": "0.6.0",
"version": "0.7.0",
"description": "deep cloning function for js objects",
"main": "dist/main.js",
"scripts": {
Expand Down
18 changes: 10 additions & 8 deletions src/deepClone.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ const updateReferences = require("./updateReferences");
const propsHandler = require("./propsHandler");

function deepClone(source, config) {
// circular references guard
// already visited references map
// each analized object will store its reference here
// so we can check each of its object properties to see if there are
// reference to already analized objects
// so we can check each of its chilren object to see if there are
// references to already analized objects
const references = new WeakMap();

// A reference to the initial source object
Expand Down Expand Up @@ -102,13 +102,15 @@ function deepClone(source, config) {
);
}

// each time an object is cloned I update the references map
// with its new reference. The object could still have some old circ refs
// but I'll handle this later
references.set(source, res);

// circular references update from temp old values to new ones
// we don't it if allowCircularReferences is false because the previous check
// in omniclone.js would have trown an error
if (allowCircularReferences) {
// each time an object is cloned I update the references map
// with its new reference. The object could still have some old circ refs
// but I'll handle this later
references.set(source, res);

if (start === source) {
// if I've recursively handled all 'virtual' children
// I've completely updated the references map
Expand Down
38 changes: 6 additions & 32 deletions src/handlers/mapEntriesHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ const errorObjectsHandler = require("./errorsObjectsHandler");
const regexpObjectsHandler = require("./regexpObjectsHanlder");
const dateObjectsHandler = require("./dateObjectsHandler");
const primitiveObjectsHandler = require("./primitiveObjectsHandler");
const circReferencesHelper = require("./../utility/circReferencesHelper");
const safeReferencesHelper = require("./../utility/safeReferencesHelper");
const prevReferencesHelper = require("./../utility/prevReferencesHelper");

module.exports = (
res,
Expand All @@ -16,42 +15,17 @@ module.exports = (
) => {
const mapEntries = data;

const { allowCircularReferences, discardErrorObjects } = config;
const { discardErrorObjects } = config;

for (const [key, value] of mapEntries) {
if (value && typeof value === "object") {
// check for duplicated sibiling object references
const safeReference = safeReferencesHelper(safeReferences, value);
if (safeReference) {
res.set(key, safeReference);
// check if I've already found this object
const prevRef = prevReferencesHelper(references, value);
if (prevRef) {
res.set(key, prevRef);
continue;
}

// check for circular references
const circRef = circReferencesHelper(
references,
value,
allowCircularReferences
);
if (circRef) {
res.set(key, circRef);
continue;
}

if (references.has(value)) {
if (!allowCircularReferences) {
throw new TypeError("TypeError: circular reference found");
} else {
// if circulary references are allowed
// the temporary result is exactly the circ referred object
// it could be an 'old' object (map included)
// or an already copied object with or without
// some 'old' circ references inside it
res.set(key, references.get(value));
continue;
}
}

// check discardErrorObjects flag to see how to handle Error objects
if (value instanceof Error) {
errorObjectsHandler(discardErrorObjects);
Expand Down
23 changes: 5 additions & 18 deletions src/handlers/otherObjectsDescriptorsHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ const errorObjectsHandler = require("./errorsObjectsHandler");
const regexpObjectsHandler = require("./regexpObjectsHanlder");
const dateObjectsHandler = require("./dateObjectsHandler");
const primitiveObjectsHandler = require("./primitiveObjectsHandler");
const circReferencesHelper = require("./../utility/circReferencesHelper");
const safeReferencesHelper = require("./../utility/safeReferencesHelper");
const prevReferencesHelper = require("./../utility/prevReferencesHelper");

module.exports = (
res,
Expand All @@ -20,7 +19,6 @@ module.exports = (
copyNonEnumerables,
copySymbols,
copyGettersSetters,
allowCircularReferences,
discardErrorObjects
} = config;

Expand All @@ -44,21 +42,10 @@ module.exports = (
if (!copyGettersSetters && (descriptor.get || descriptor.set)) return;

if (value && typeof value === "object") {
// check for duplicated sibiling object references
const safeReference = safeReferencesHelper(safeReferences, value);
if (safeReference) {
res[prop] = safeReference;
return;
}

// check for circular references -
const circRef = circReferencesHelper(
references,
value,
allowCircularReferences
);
if (circRef) {
res[prop] = circRef;
// check if I've already found this object
const prevRef = prevReferencesHelper(references, value);
if (prevRef) {
res[prop] = prevRef;
return;
}

Expand Down
16 changes: 6 additions & 10 deletions src/handlers/setEntriesHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const errorObjectsHandler = require("./errorsObjectsHandler");
const regexpObjectsHandler = require("./regexpObjectsHanlder");
const dateObjectsHandler = require("./dateObjectsHandler");
const primitiveObjectsHandler = require("./primitiveObjectsHandler");
const circReferencesHelper = require("./../utility/circReferencesHelper");
const prevReferencesHelper = require("./../utility/prevReferencesHelper");

module.exports = (
res,
Expand All @@ -14,20 +14,16 @@ module.exports = (
) => {
const setEntries = data;

const { allowCircularReferences, discardErrorObjects } = config;
const { discardErrorObjects } = config;

// for set key == value

for (const value of setEntries) {
if (value && typeof value === "object") {
// check for circular references
const circRef = circReferencesHelper(
references,
value,
allowCircularReferences
);
if (circRef) {
res.add(circRef);
// check if I've already found this object
const prevRef = prevReferencesHelper(references, value);
if (prevRef) {
res.add(prevRef);
continue;
}

Expand Down
5 changes: 3 additions & 2 deletions src/omniclone.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ function omniclone(
copyNonEnumerables = false,
copySymbols = false,
copyGettersSetters = false,
allowCircularReferences = false,
allowCircularReferences = true,
discardErrorObjects = true
} = {}
) {
Expand Down Expand Up @@ -85,7 +85,8 @@ function omniclone(
// so we have to force the allowCircularReferences if there are not
const depsMap = createDependenciesMap(obj, copyNonEnumerables, copySymbols);
// eslint-disable-next-line no-param-reassign
allowCircularReferences = checkCircRef(depsMap);
if (checkCircRef(depsMap))
throw new TypeError("TypeError: circular reference found");
}

const config = {
Expand Down
16 changes: 0 additions & 16 deletions src/utility/circReferencesHelper.js

This file was deleted.

2 changes: 1 addition & 1 deletion src/utility/dependenciesMapHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ function dependenciesMapHandler(map) {
}

function checkCircRefs(map) {
return !dependenciesMapHandler(map).size;
return dependenciesMapHandler(map).size;
}

module.exports = checkCircRefs;
8 changes: 8 additions & 0 deletions src/utility/prevReferencesHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = (references, value) => {
if (references.has(value)) {
// if I've previously found thid object value
// I can return it because I've stored it in the references map
return references.get(value); // is an object, that is always a truthy value
}
return null;
};

0 comments on commit 0aee706

Please sign in to comment.