pvorb / clone

deeply clone arbitrary objects in javascript
https://www.npmjs.com/package/clone
MIT License
781 stars 129 forks source link

Enable clone to be extended. #108

Open btsimonh opened 5 years ago

btsimonh commented 5 years ago

replaces https://github.com/pvorb/clone/pull/93 - but allows for the same kind of global modification of clone behaviour.

add clone.globalopts containing _clone. If set, globalopts._clone is called with each item at the start of _clone().
If it returns undefined, then the normal _clone() continues.
If it returns something, the result is is used as the item being cloned, and the rest of _clone() is skipped.

ToDo:

Thanks!

btsimonh commented 5 years ago

ref the end of https://github.com/pvorb/clone/issues/106 :

this mod may be demonstrated using the below code:

// get a first copy of clone
var clone = require('clone');
clone.clone1 = true;

// get a second copy of clone
var clone2 = require('clone');

// setup for a fail by spawning a process to get a ChildProcess object
var cp = require('child_process');
var spawn = cp.spawn;
var child = spawn('cmd', [], {});

// the object we will try to clone.
var obj = {
  child: child,
  teststruct: {
    willchange: 1,
  }
};

// simplest example
clone.globalopts._clone = function(parent, depth){
  if (parent._pleasedontcopyme){
    return parent;
  } else {
    return;
  }
}

// tell the globalopts._clone function that the ChildProcess object should be copied by reference, not cloned.
obj.child._pleasedontcopyme = true;

// should succeed
console.log("will succeed - but is a ref copy, not a clone");
var copy = clone(obj);
obj.teststruct.willchange = 2;
console.log("obj.teststruct.willchange should be 1");
console.log(copy);
delete obj.child._pleasedontcopyme;

// complex example
clone.globalopts._clone = function(parent, depth){
  if (parent._clone && (typeof parent._clone === 'function')) {
    return parent._clone(parent, depth);
  } else {
    return;
  }
}

// completely replace the object with something else
obj.child._clone = function(parent, depth){
  return { message: "object could not be cloned" };
}

// test that the globalopts structure applies to our second clone require:
console.log("will succeed - but child is replaced, not a clone");
var copy1a = clone2(obj);
obj.teststruct.willchange = 18;
console.log("obj.teststruct.willchange should be 2, not 18");
console.log(copy1a);

// may kill nodejs/V8
// remove patch from clone:
clone.globalopts._clone = null;

console.log("may kill nodejs/V8 - because cloning this type of object is not supported");
var copy2 = clone(obj);
obj.teststruct.willchange = 3;
console.log("obj.teststruct.willchange should be 1");
console.log(copy2);
btsimonh commented 5 years ago

@pvorb - if uyou like this PR, then just say and I'll add to the readme. Are checks already broken, because I don't see how this change can have caused the failure it's stating?

btsimonh commented 5 years ago

The V8 crash protection is based on a simple list of '[object Type]' strings, and is in the extensible portion, so anyone can enhance the protection as more types are discovered that can't be cloned. e.g. from user code, clone.globalopts.crash_types['[object Socket]'] = true; would make Sockets be referenced, not cloned.

or indeed, in my case clone.globalopts.crash_types['object ChildProcess'] = true;