You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					143 lines
				
				3.6 KiB
			
		
		
			
		
	
	
					143 lines
				
				3.6 KiB
			| 
											3 years ago
										 | 'use strict'; | ||
|  | 
 | ||
|  | 
 | ||
|  | const cloneRegExp = require('regexp-clone'); | ||
|  | const Decimal = require('../types/decimal128'); | ||
|  | const ObjectId = require('../types/objectid'); | ||
|  | const specialProperties = require('./specialProperties'); | ||
|  | const isMongooseObject = require('./isMongooseObject'); | ||
|  | const getFunctionName = require('./getFunctionName'); | ||
|  | const isBsonType = require('./isBsonType'); | ||
|  | const isObject = require('./isObject'); | ||
|  | const symbols = require('./symbols'); | ||
|  | const utils = require('../utils'); | ||
|  | 
 | ||
|  | 
 | ||
|  | /*! | ||
|  |  * Object clone with Mongoose natives support. | ||
|  |  * | ||
|  |  * If options.minimize is true, creates a minimal data object. Empty objects and undefined values will not be cloned. This makes the data payload sent to MongoDB as small as possible. | ||
|  |  * | ||
|  |  * Functions are never cloned. | ||
|  |  * | ||
|  |  * @param {Object} obj the object to clone | ||
|  |  * @param {Object} options | ||
|  |  * @param {Boolean} isArrayChild true if cloning immediately underneath an array. Special case for minimize. | ||
|  |  * @return {Object} the cloned object | ||
|  |  * @api private | ||
|  |  */ | ||
|  | 
 | ||
|  | function clone(obj, options, isArrayChild) { | ||
|  |   if (obj == null) { | ||
|  |     return obj; | ||
|  |   } | ||
|  | 
 | ||
|  |   if (Array.isArray(obj)) { | ||
|  |     return cloneArray(obj, options); | ||
|  |   } | ||
|  | 
 | ||
|  |   if (isMongooseObject(obj)) { | ||
|  |     // Single nested subdocs should apply getters later in `applyGetters()`
 | ||
|  |     // when calling `toObject()`. See gh-7442, gh-8295
 | ||
|  |     if (options && options._skipSingleNestedGetters && obj.$isSingleNested) { | ||
|  |       options = Object.assign({}, options, { getters: false }); | ||
|  |     } | ||
|  | 
 | ||
|  |     if (utils.isPOJO(obj) && obj.$__ != null && obj._doc != null) { | ||
|  |       return obj._doc; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (options && options.json && typeof obj.toJSON === 'function') { | ||
|  |       return obj.toJSON(options); | ||
|  |     } | ||
|  |     return obj.toObject(options); | ||
|  |   } | ||
|  | 
 | ||
|  |   if (obj.constructor) { | ||
|  |     switch (getFunctionName(obj.constructor)) { | ||
|  |       case 'Object': | ||
|  |         return cloneObject(obj, options, isArrayChild); | ||
|  |       case 'Date': | ||
|  |         return new obj.constructor(+obj); | ||
|  |       case 'RegExp': | ||
|  |         return cloneRegExp(obj); | ||
|  |       default: | ||
|  |         // ignore
 | ||
|  |         break; | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   if (obj instanceof ObjectId) { | ||
|  |     return new ObjectId(obj.id); | ||
|  |   } | ||
|  | 
 | ||
|  |   if (isBsonType(obj, 'Decimal128')) { | ||
|  |     if (options && options.flattenDecimals) { | ||
|  |       return obj.toJSON(); | ||
|  |     } | ||
|  |     return Decimal.fromString(obj.toString()); | ||
|  |   } | ||
|  | 
 | ||
|  |   if (!obj.constructor && isObject(obj)) { | ||
|  |     // object created with Object.create(null)
 | ||
|  |     return cloneObject(obj, options, isArrayChild); | ||
|  |   } | ||
|  | 
 | ||
|  |   if (obj[symbols.schemaTypeSymbol]) { | ||
|  |     return obj.clone(); | ||
|  |   } | ||
|  | 
 | ||
|  |   // If we're cloning this object to go into a MongoDB command,
 | ||
|  |   // and there's a `toBSON()` function, assume this object will be
 | ||
|  |   // stored as a primitive in MongoDB and doesn't need to be cloned.
 | ||
|  |   if (options && options.bson && typeof obj.toBSON === 'function') { | ||
|  |     return obj; | ||
|  |   } | ||
|  | 
 | ||
|  |   if (obj.valueOf != null) { | ||
|  |     return obj.valueOf(); | ||
|  |   } | ||
|  | 
 | ||
|  |   return cloneObject(obj, options, isArrayChild); | ||
|  | } | ||
|  | module.exports = clone; | ||
|  | 
 | ||
|  | /*! | ||
|  |  * ignore | ||
|  |  */ | ||
|  | 
 | ||
|  | function cloneObject(obj, options, isArrayChild) { | ||
|  |   const minimize = options && options.minimize; | ||
|  |   const ret = {}; | ||
|  |   let hasKeys; | ||
|  | 
 | ||
|  |   for (const k of Object.keys(obj)) { | ||
|  |     if (specialProperties.has(k)) { | ||
|  |       continue; | ||
|  |     } | ||
|  | 
 | ||
|  |     // Don't pass `isArrayChild` down
 | ||
|  |     const val = clone(obj[k], options); | ||
|  | 
 | ||
|  |     if (!minimize || (typeof val !== 'undefined')) { | ||
|  |       if (minimize === false && typeof val === 'undefined') { | ||
|  |         delete ret[k]; | ||
|  |       } else { | ||
|  |         hasKeys || (hasKeys = true); | ||
|  |         ret[k] = val; | ||
|  |       } | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   return minimize && !isArrayChild ? hasKeys && ret : ret; | ||
|  | } | ||
|  | 
 | ||
|  | function cloneArray(arr, options) { | ||
|  |   const ret = []; | ||
|  | 
 | ||
|  |   for (const item of arr) { | ||
|  |     ret.push(clone(item, options, true)); | ||
|  |   } | ||
|  | 
 | ||
|  |   return ret; | ||
|  | } |