chainy/node_modules/javascript-stringify/javascript-stringify.js

385 lines
10 KiB
JavaScript
Raw Normal View History

2020-01-26 19:03:32 +00:00
(function (root, stringify) {
/* istanbul ignore else */
if (typeof require === 'function' && typeof exports === 'object' && typeof module === 'object') {
// Node.
module.exports = stringify();
} else if (typeof define === 'function' && define.amd) {
// AMD, registers as an anonymous module.
define(function () {
return stringify();
});
} else {
// Browser global.
root.javascriptStringify = stringify();
}
})(this, function () {
/**
* Match all characters that need to be escaped in a string. Modified from
* source to match single quotes instead of double.
*
* Source: https://github.com/douglascrockford/JSON-js/blob/master/json2.js
*/
var ESCAPABLE = /[\\\'\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
/**
* Map of characters to escape characters.
*/
var META_CHARS = {
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
"'": "\\'",
'"': '\\"',
'\\': '\\\\'
};
/**
* Escape any character into its literal JavaScript string.
*
* @param {string} char
* @return {string}
*/
function escapeChar (char) {
var meta = META_CHARS[char];
return meta || '\\u' + ('0000' + char.charCodeAt(0).toString(16)).slice(-4);
};
/**
* JavaScript reserved word list.
*/
var RESERVED_WORDS = {};
/**
* Map reserved words to the object.
*/
(
'break else new var case finally return void catch for switch while ' +
'continue function this with default if throw delete in try ' +
'do instanceof typeof abstract enum int short boolean export ' +
'interface static byte extends long super char final native synchronized ' +
'class float package throws const goto private transient debugger ' +
'implements protected volatile double import public let yield'
).split(' ').map(function (key) {
RESERVED_WORDS[key] = true;
});
/**
* Test for valid JavaScript identifier.
*/
var IS_VALID_IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
/**
* Check if a variable name is valid.
*
* @param {string} name
* @return {boolean}
*/
function isValidVariableName (name) {
return !RESERVED_WORDS[name] && IS_VALID_IDENTIFIER.test(name);
}
/**
* Return the global variable name.
*
* @return {string}
*/
function toGlobalVariable (value) {
return 'Function(' + stringify('return this;') + ')()';
}
/**
* Serialize the path to a string.
*
* @param {Array} path
* @return {string}
*/
function toPath (path) {
var result = '';
for (var i = 0; i < path.length; i++) {
if (isValidVariableName(path[i])) {
result += '.' + path[i];
} else {
result += '[' + stringify(path[i]) + ']';
}
}
return result;
}
/**
* Stringify an array of values.
*
* @param {Array} array
* @param {string} indent
* @param {Function} next
* @return {string}
*/
function stringifyArray (array, indent, next) {
// Map array values to their stringified values with correct indentation.
var values = array.map(function (value, index) {
var str = next(value, index);
if (str === undefined) {
return String(str);
}
return indent + str.split('\n').join('\n' + indent);
}).join(indent ? ',\n' : ',');
// Wrap the array in newlines if we have indentation set.
if (indent && values) {
return '[\n' + values + '\n]';
}
return '[' + values + ']';
}
/**
* Stringify a map of values.
*
* @param {Object} object
* @param {string} indent
* @param {Function} next
* @return {string}
*/
function stringifyObject (object, indent, next) {
// Iterate over object keys and concat string together.
var values = Object.keys(object).reduce(function (values, key) {
var value = next(object[key], key);
// Omit `undefined` object values.
if (value === undefined) {
return values;
}
// String format the key and value data.
key = isValidVariableName(key) ? key : stringify(key);
value = String(value).split('\n').join('\n' + indent);
// Push the current object key and value into the values array.
values.push(indent + key + ':' + (indent ? ' ' : '') + value);
return values;
}, []).join(indent ? ',\n' : ',');
// Wrap the object in newlines if we have indentation set.
if (indent && values) {
return '{\n' + values + '\n}';
}
return '{' + values + '}';
}
/**
* Convert JavaScript objects into strings.
*/
var OBJECT_TYPES = {
'[object Array]': stringifyArray,
'[object Object]': stringifyObject,
'[object Error]': function (error) {
return 'new Error(' + stringify(error.message) + ')';
},
'[object Date]': function (date) {
return 'new Date(' + date.getTime() + ')';
},
'[object String]': function (string) {
return 'new String(' + stringify(string.toString()) + ')';
},
'[object Number]': function (number) {
return 'new Number(' + number + ')';
},
'[object Boolean]': function (boolean) {
return 'new Boolean(' + boolean + ')';
},
'[object Uint8Array]': function (array, indent) {
return 'new Uint8Array(' + stringifyArray(array) + ')';
},
'[object Set]': function (array, indent, next) {
if (typeof Array.from === 'function') {
return 'new Set(' + stringify(Array.from(array), indent, next) + ')';
} else return undefined;
},
'[object Map]': function (array, indent, next) {
if (typeof Array.from === 'function') {
return 'new Map(' + stringify(Array.from(array), indent, next) + ')';
} else return undefined;
},
'[object RegExp]': String,
'[object Function]': String,
'[object global]': toGlobalVariable,
'[object Window]': toGlobalVariable
};
/**
* Convert JavaScript primitives into strings.
*/
var PRIMITIVE_TYPES = {
'string': function (string) {
return "'" + string.replace(ESCAPABLE, escapeChar) + "'";
},
'number': String,
'object': String,
'boolean': String,
'symbol': String,
'undefined': String
};
/**
* Convert any value to a string.
*
* @param {*} value
* @param {string} indent
* @param {Function} next
* @return {string}
*/
function stringify (value, indent, next) {
// Convert primitives into strings.
if (Object(value) !== value) {
return PRIMITIVE_TYPES[typeof value](value, indent, next);
}
// Handle buffer objects before recursing (node < 6 was an object, node >= 6 is a `Uint8Array`).
if (typeof Buffer === 'function' && Buffer.isBuffer(value)) {
return 'new Buffer(' + next(value.toString()) + ')';
}
// Use the internal object string to select stringification method.
var toString = OBJECT_TYPES[Object.prototype.toString.call(value)];
// Convert objects into strings.
return toString ? toString(value, indent, next) : undefined;
}
/**
* Stringify an object into the literal string.
*
* @param {*} value
* @param {Function} [replacer]
* @param {(number|string)} [space]
* @param {Object} [options]
* @return {string}
*/
return function (value, replacer, space, options) {
options = options || {}
// Convert the spaces into a string.
if (typeof space !== 'string') {
space = new Array(Math.max(0, space|0) + 1).join(' ');
}
var maxDepth = Number(options.maxDepth) || 100;
var references = !!options.references;
var skipUndefinedProperties = !!options.skipUndefinedProperties;
var valueCount = Number(options.maxValues) || 100000;
var path = [];
var stack = [];
var encountered = [];
var paths = [];
var restore = [];
/**
* Stringify the next value in the stack.
*
* @param {*} value
* @param {string} key
* @return {string}
*/
function next (value, key) {
if (skipUndefinedProperties && value === undefined) {
return undefined;
}
path.push(key);
var result = recurse(value, stringify);
path.pop();
return result;
}
/**
* Handle recursion by checking if we've visited this node every iteration.
*
* @param {*} value
* @param {Function} stringify
* @return {string}
*/
var recurse = references ?
function (value, stringify) {
if (value && (typeof value === 'object' || typeof value === 'function')) {
var seen = encountered.indexOf(value);
// Track nodes to restore later.
if (seen > -1) {
restore.push(path.slice(), paths[seen]);
return;
}
// Track encountered nodes.
encountered.push(value);
paths.push(path.slice());
}
// Stop when we hit the max depth.
if (path.length > maxDepth || valueCount-- <= 0) {
return;
}
// Stringify the value and fallback to
return stringify(value, space, next);
} :
function (value, stringify) {
var seen = stack.indexOf(value);
if (seen > -1 || path.length > maxDepth || valueCount-- <= 0) {
return;
}
stack.push(value);
var value = stringify(value, space, next);
stack.pop();
return value;
};
// If the user defined a replacer function, make the recursion function
// a double step process - `recurse -> replacer -> stringify`.
if (typeof replacer === 'function') {
var before = recurse
// Intertwine the replacer function with the regular recursion.
recurse = function (value, stringify) {
return before(value, function (value, space, next) {
return replacer(value, space, function (value) {
return stringify(value, space, next);
});
});
};
}
var result = recurse(value, stringify);
// Attempt to restore circular references.
if (restore.length) {
var sep = space ? '\n' : '';
var assignment = space ? ' = ' : '=';
var eol = ';' + sep;
var before = space ? '(function () {' : '(function(){'
var after = '}())'
var results = ['var x' + assignment + result];
for (var i = 0; i < restore.length; i += 2) {
results.push('x' + toPath(restore[i]) + assignment + 'x' + toPath(restore[i + 1]));
}
results.push('return x');
return before + sep + results.join(eol) + eol + after
}
return result;
};
});