javascript-stringify
Advanced tools
Comparing version 1.2.0 to 1.3.0
@@ -1,3 +0,10 @@ | ||
declare function javascriptStringify (value: any, replacer?: Function, space?: string | number): string; | ||
declare function stringify (value: any, replacer?: Function, space?: string | number, options?: javascriptStringify.Options): string; | ||
export = javascriptStringify; | ||
declare namespace javascriptStringify { | ||
export interface Options { | ||
maxDepth?: number; | ||
references?: boolean; | ||
} | ||
} | ||
export = stringify; |
@@ -21,4 +21,2 @@ (function (root, stringify) { | ||
* Source: https://github.com/douglascrockford/JSON-js/blob/master/json2.js | ||
* | ||
* @type {RegExp} | ||
*/ | ||
@@ -29,4 +27,2 @@ 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. | ||
* | ||
* @type {Object} | ||
*/ | ||
@@ -47,6 +43,6 @@ var META_CHARS = { | ||
* | ||
* @param {String} char | ||
* @return {String} | ||
* @param {string} char | ||
* @return {string} | ||
*/ | ||
var escapeChar = function (char) { | ||
function escapeChar (char) { | ||
var meta = META_CHARS[char]; | ||
@@ -77,10 +73,15 @@ | ||
/** | ||
* 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} | ||
* @param {string} name | ||
* @return {boolean} | ||
*/ | ||
var isValidVariableName = function (name) { | ||
return !RESERVED_WORDS[name] && /^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(name); | ||
}; | ||
function isValidVariableName (name) { | ||
return !RESERVED_WORDS[name] && IS_VALID_IDENTIFIER.test(name); | ||
} | ||
@@ -90,18 +91,36 @@ /** | ||
* | ||
* @return {String} | ||
* @return {string} | ||
*/ | ||
var toGlobalVariable = function (value, indent, stringify) { | ||
function toGlobalVariable (value) { | ||
return 'Function(' + stringify('return this;') + ')()'; | ||
}; | ||
} | ||
/** | ||
* Convert JavaScript objects into strings. | ||
* Serialize the path to a string. | ||
* | ||
* @type {Object} | ||
* @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; | ||
} | ||
/** | ||
* Convert JavaScript objects into strings. | ||
*/ | ||
var OBJECT_TYPES = { | ||
'[object Array]': function (array, indent, stringify) { | ||
'[object Array]': function (array, indent, next) { | ||
// Map array values to their stringified values with correct indentation. | ||
var values = array.map(function (value) { | ||
var str = stringify(value); | ||
var values = array.map(function (value, index) { | ||
var str = next(value, index); | ||
@@ -122,3 +141,3 @@ if (str === undefined) { | ||
}, | ||
'[object Object]': function (object, indent, stringify) { | ||
'[object Object]': function (object, indent, next) { | ||
if (typeof Buffer === 'function' && Buffer.isBuffer(object)) { | ||
@@ -130,3 +149,3 @@ return 'new Buffer(' + stringify(object.toString()) + ')'; | ||
var values = Object.keys(object).reduce(function (values, key) { | ||
var value = stringify(object[key]); | ||
var value = next(object[key], key); | ||
@@ -192,4 +211,2 @@ // Omit `undefined` object values. | ||
* Convert JavaScript primitives into strings. | ||
* | ||
* @type {Object} | ||
*/ | ||
@@ -211,10 +228,10 @@ var PRIMITIVE_TYPES = { | ||
* @param {*} value | ||
* @param {String} indent | ||
* @param {Function} stringify | ||
* @return {String} | ||
* @param {string} indent | ||
* @param {Function} next | ||
* @return {string} | ||
*/ | ||
var stringify = function (value, indent, stringify) { | ||
function stringify (value, indent, next) { | ||
// Convert primitives into strings. | ||
if (Object(value) !== value) { | ||
return PRIMITIVE_TYPES[typeof value](value, indent, stringify); | ||
return PRIMITIVE_TYPES[typeof value](value, indent, next); | ||
} | ||
@@ -226,4 +243,4 @@ | ||
// Convert objects into strings. | ||
return toString && toString(value, indent, stringify); | ||
}; | ||
return toString ? toString(value, indent, next) : undefined; | ||
} | ||
@@ -233,7 +250,7 @@ /** | ||
* | ||
* @param {Object} value | ||
* @param {*} value | ||
* @param {Function} [replacer] | ||
* @param {(Number|String)} [space] | ||
* @param {Object} [options] | ||
* @return {String} | ||
* @param {(number|string)} [space] | ||
* @param {Object} [options] | ||
* @return {string} | ||
*/ | ||
@@ -248,46 +265,106 @@ return function (value, replacer, space, options) { | ||
var maxDepth = options.maxDepth || 200; | ||
var maxDepth = Number(options.maxDepth) || 100; | ||
var references = !!options.references; | ||
var depth = 0; | ||
var cache = []; | ||
var path = []; | ||
var stack = []; | ||
var encountered = []; | ||
var paths = []; | ||
var restore = []; | ||
/** | ||
* Handle recursion by checking if we've visited this node every iteration. | ||
* Stringify the next value in the stack. | ||
* | ||
* @param {*} value | ||
* @return {String} | ||
* @param {string} key | ||
* @return {string} | ||
*/ | ||
var recurse = function (value, next) { | ||
// If we've already visited this node before, break the recursion. | ||
if (cache.indexOf(value) > -1 || depth > maxDepth) { | ||
return; | ||
} | ||
function next (value, key) { | ||
path.push(key); | ||
var result = recurse(value, stringify); | ||
path.pop(); | ||
return result; | ||
} | ||
// Push the value into the values cache to avoid an infinite loop. | ||
depth++; | ||
cache.push(value); | ||
/** | ||
* 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 exists = encountered.indexOf(value); | ||
// Stringify the value and fallback to | ||
return next(value, space, function (value) { | ||
var result = recurse(value, next); | ||
// Track nodes to restore later. | ||
if (exists > -1) { | ||
restore.push(path.slice(), paths[exists]); | ||
return; | ||
} | ||
depth--; | ||
cache.pop(); | ||
// Track encountered nodes. | ||
encountered.push(value); | ||
paths.push(path.slice()); | ||
} | ||
return result; | ||
}); | ||
}; | ||
// Stop when we hit the max depth. | ||
if (path.length > maxDepth) { | ||
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) { | ||
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 - `replacer -> stringify -> replacer -> etc`. | ||
// a double step process - `recurse -> replacer -> stringify`. | ||
if (typeof replacer === 'function') { | ||
return recurse(value, function (value, space, next) { | ||
return replacer(value, space, function (value) { | ||
return stringify(value, space, next); | ||
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); | ||
}); | ||
}); | ||
}); | ||
}; | ||
} | ||
return recurse(value, stringify); | ||
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; | ||
}; | ||
}); |
{ | ||
"name": "javascript-stringify", | ||
"version": "1.2.0", | ||
"version": "1.3.0", | ||
"description": "Stringify is to `eval` as `JSON.stringify` is to `JSON.parse`", | ||
@@ -5,0 +5,0 @@ "main": "javascript-stringify.js", |
@@ -8,3 +8,3 @@ # JavaScript Stringify | ||
Stringify is to `eval` as `JSON.stringify` is to `JSON.parse`. | ||
> Stringify is to `eval` as `JSON.stringify` is to `JSON.parse`. | ||
@@ -44,7 +44,8 @@ ## Installation | ||
The API is similar to `JSON.stringify`. However, any value returned by the replacer will be used literally. For this reason, the replacer is passed three arguments - `value`, `indentation` and `stringify`. If you need to continue the stringification process inside your replacer, you can call `stringify` with the updated value. | ||
The API is similar to `JSON.stringify`. However, any value returned by the replacer will be used literally. For this reason, the replacer is passed three arguments - `value`, `indentation` and `stringify`. If you need to continue the stringification process inside your replacer, you can call `stringify(value)` with the new value. | ||
The `options` object allows some additional configuration: | ||
* **maxDepth** The maximum depth to stringify to | ||
* **maxDepth** _(number)_ The maximum depth of values to stringify | ||
* **references** _(boolean)_ Restore circular/repeated references in the object (uses IIFE) | ||
@@ -51,0 +52,0 @@ ### Examples |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
15412
315
116