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.
		
		
		
		
		
			
		
			
				
					
					
						
							230 lines
						
					
					
						
							5.7 KiB
						
					
					
				
			
		
		
	
	
							230 lines
						
					
					
						
							5.7 KiB
						
					
					
				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)
 | 
						|
  }
 | 
						|
}
 |