mirror of
				https://github.com/1disk/edp445.git
				synced 2024-08-14 22:47:02 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			229 lines
		
	
	
	
		
			5.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			229 lines
		
	
	
	
		
			5.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| module.exports = stringify
 | |
| stringify.default = stringify
 | |
| stringify.stable = deterministicStringify
 | |
| stringify.stableStringify = deterministicStringify
 | |
| 
 | |
| var LIMIT_REPLACE_NODE = '[...]'
 | |
| var CIRCULAR_REPLACE_NODE = '[Circular]'
 | |
| 
 | |
| var arr = []
 | |
| var replacerStack = []
 | |
| 
 | |
| function defaultOptions () {
 | |
|   return {
 | |
|     depthLimit: Number.MAX_SAFE_INTEGER,
 | |
|     edgesLimit: Number.MAX_SAFE_INTEGER
 | |
|   }
 | |
| }
 | |
| 
 | |
| // Regular stringify
 | |
| function stringify (obj, replacer, spacer, options) {
 | |
|   if (typeof options === 'undefined') {
 | |
|     options = defaultOptions()
 | |
|   }
 | |
| 
 | |
|   decirc(obj, '', 0, [], undefined, 0, options)
 | |
|   var res
 | |
|   try {
 | |
|     if (replacerStack.length === 0) {
 | |
|       res = JSON.stringify(obj, replacer, spacer)
 | |
|     } else {
 | |
|       res = JSON.stringify(obj, replaceGetterValues(replacer), spacer)
 | |
|     }
 | |
|   } catch (_) {
 | |
|     return JSON.stringify('[unable to serialize, circular reference is too complex to analyze]')
 | |
|   } finally {
 | |
|     while (arr.length !== 0) {
 | |
|       var part = arr.pop()
 | |
|       if (part.length === 4) {
 | |
|         Object.defineProperty(part[0], part[1], part[3])
 | |
|       } else {
 | |
|         part[0][part[1]] = part[2]
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return res
 | |
| }
 | |
| 
 | |
| function setReplace (replace, val, k, parent) {
 | |
|   var propertyDescriptor = Object.getOwnPropertyDescriptor(parent, k)
 | |
|   if (propertyDescriptor.get !== undefined) {
 | |
|     if (propertyDescriptor.configurable) {
 | |
|       Object.defineProperty(parent, k, { value: replace })
 | |
|       arr.push([parent, k, val, propertyDescriptor])
 | |
|     } else {
 | |
|       replacerStack.push([val, k, replace])
 | |
|     }
 | |
|   } else {
 | |
|     parent[k] = replace
 | |
|     arr.push([parent, k, val])
 | |
|   }
 | |
| }
 | |
| 
 | |
| function decirc (val, k, edgeIndex, stack, parent, depth, options) {
 | |
|   depth += 1
 | |
|   var i
 | |
|   if (typeof val === 'object' && val !== null) {
 | |
|     for (i = 0; i < stack.length; i++) {
 | |
|       if (stack[i] === val) {
 | |
|         setReplace(CIRCULAR_REPLACE_NODE, val, k, parent)
 | |
|         return
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (
 | |
|       typeof options.depthLimit !== 'undefined' &&
 | |
|       depth > options.depthLimit
 | |
|     ) {
 | |
|       setReplace(LIMIT_REPLACE_NODE, val, k, parent)
 | |
|       return
 | |
|     }
 | |
| 
 | |
|     if (
 | |
|       typeof options.edgesLimit !== 'undefined' &&
 | |
|       edgeIndex + 1 > options.edgesLimit
 | |
|     ) {
 | |
|       setReplace(LIMIT_REPLACE_NODE, val, k, parent)
 | |
|       return
 | |
|     }
 | |
| 
 | |
|     stack.push(val)
 | |
|     // Optimize for Arrays. Big arrays could kill the performance otherwise!
 | |
|     if (Array.isArray(val)) {
 | |
|       for (i = 0; i < val.length; i++) {
 | |
|         decirc(val[i], i, i, stack, val, depth, options)
 | |
|       }
 | |
|     } else {
 | |
|       var keys = Object.keys(val)
 | |
|       for (i = 0; i < keys.length; i++) {
 | |
|         var key = keys[i]
 | |
|         decirc(val[key], key, i, stack, val, depth, options)
 | |
|       }
 | |
|     }
 | |
|     stack.pop()
 | |
|   }
 | |
| }
 | |
| 
 | |
| // Stable-stringify
 | |
| function compareFunction (a, b) {
 | |
|   if (a < b) {
 | |
|     return -1
 | |
|   }
 | |
|   if (a > b) {
 | |
|     return 1
 | |
|   }
 | |
|   return 0
 | |
| }
 | |
| 
 | |
| function deterministicStringify (obj, replacer, spacer, options) {
 | |
|   if (typeof options === 'undefined') {
 | |
|     options = defaultOptions()
 | |
|   }
 | |
| 
 | |
|   var tmp = deterministicDecirc(obj, '', 0, [], undefined, 0, options) || obj
 | |
|   var res
 | |
|   try {
 | |
|     if (replacerStack.length === 0) {
 | |
|       res = JSON.stringify(tmp, replacer, spacer)
 | |
|     } else {
 | |
|       res = JSON.stringify(tmp, replaceGetterValues(replacer), spacer)
 | |
|     }
 | |
|   } catch (_) {
 | |
|     return JSON.stringify('[unable to serialize, circular reference is too complex to analyze]')
 | |
|   } finally {
 | |
|     // Ensure that we restore the object as it was.
 | |
|     while (arr.length !== 0) {
 | |
|       var part = arr.pop()
 | |
|       if (part.length === 4) {
 | |
|         Object.defineProperty(part[0], part[1], part[3])
 | |
|       } else {
 | |
|         part[0][part[1]] = part[2]
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return res
 | |
| }
 | |
| 
 | |
| function deterministicDecirc (val, k, edgeIndex, stack, parent, depth, options) {
 | |
|   depth += 1
 | |
|   var i
 | |
|   if (typeof val === 'object' && val !== null) {
 | |
|     for (i = 0; i < stack.length; i++) {
 | |
|       if (stack[i] === val) {
 | |
|         setReplace(CIRCULAR_REPLACE_NODE, val, k, parent)
 | |
|         return
 | |
|       }
 | |
|     }
 | |
|     try {
 | |
|       if (typeof val.toJSON === 'function') {
 | |
|         return
 | |
|       }
 | |
|     } catch (_) {
 | |
|       return
 | |
|     }
 | |
| 
 | |
|     if (
 | |
|       typeof options.depthLimit !== 'undefined' &&
 | |
|       depth > options.depthLimit
 | |
|     ) {
 | |
|       setReplace(LIMIT_REPLACE_NODE, val, k, parent)
 | |
|       return
 | |
|     }
 | |
| 
 | |
|     if (
 | |
|       typeof options.edgesLimit !== 'undefined' &&
 | |
|       edgeIndex + 1 > options.edgesLimit
 | |
|     ) {
 | |
|       setReplace(LIMIT_REPLACE_NODE, val, k, parent)
 | |
|       return
 | |
|     }
 | |
| 
 | |
|     stack.push(val)
 | |
|     // Optimize for Arrays. Big arrays could kill the performance otherwise!
 | |
|     if (Array.isArray(val)) {
 | |
|       for (i = 0; i < val.length; i++) {
 | |
|         deterministicDecirc(val[i], i, i, stack, val, depth, options)
 | |
|       }
 | |
|     } else {
 | |
|       // Create a temporary object in the required way
 | |
|       var tmp = {}
 | |
|       var keys = Object.keys(val).sort(compareFunction)
 | |
|       for (i = 0; i < keys.length; i++) {
 | |
|         var key = keys[i]
 | |
|         deterministicDecirc(val[key], key, i, stack, val, depth, options)
 | |
|         tmp[key] = val[key]
 | |
|       }
 | |
|       if (typeof parent !== 'undefined') {
 | |
|         arr.push([parent, k, val])
 | |
|         parent[k] = tmp
 | |
|       } else {
 | |
|         return tmp
 | |
|       }
 | |
|     }
 | |
|     stack.pop()
 | |
|   }
 | |
| }
 | |
| 
 | |
| // wraps replacer function to handle values we couldn't replace
 | |
| // and mark them as replaced value
 | |
| function replaceGetterValues (replacer) {
 | |
|   replacer =
 | |
|     typeof replacer !== 'undefined'
 | |
|       ? replacer
 | |
|       : function (k, v) {
 | |
|         return v
 | |
|       }
 | |
|   return function (key, val) {
 | |
|     if (replacerStack.length > 0) {
 | |
|       for (var i = 0; i < replacerStack.length; i++) {
 | |
|         var part = replacerStack[i]
 | |
|         if (part[1] === key && part[0] === val) {
 | |
|           val = part[2]
 | |
|           replacerStack.splice(i, 1)
 | |
|           break
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     return replacer.call(this, key, val)
 | |
|   }
 | |
| }
 |