Socket
Socket
Sign inDemoInstall

json-dry

Package Overview
Dependencies
0
Maintainers
1
Versions
25
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.0.10 to 1.0.11

6

CHANGELOG.md

@@ -0,1 +1,7 @@

## 1.0.11 (2019-11-22)
* Split up `dryReplacer` function, added `replaceObject` function
* Don't assign regenerated value if it's exactly the same, prevents HTMLCollection errors
* Fix throwing an error when serializing an invalid date
## 1.0.10 (2019-01-31)

@@ -2,0 +8,0 @@

472

lib/json-dry.js

@@ -28,3 +28,3 @@ "use strict";

seen_path,
is_root = true,
flags = {is_root: true},
chain = [],

@@ -38,11 +38,2 @@ path = [],

var class_name,
new_value,
replaced,
is_array,
is_wrap,
keys,
key,
i;
// Process the value to a possible given replacer function

@@ -58,2 +49,4 @@ if (replacer != null) {

let is_wrap;
// An explicitly false key means this dryReplaced was

@@ -85,213 +78,266 @@ // recursively called with a replacement object

case 'object':
value = replaceObject(dryReplacer, value, chain, flags, is_wrap, holder, path, value_paths, key);
break;
if (value.constructor) {
class_name = value.constructor.name;
} else {
class_name = 'Object';
case 'string':
// Make sure regular strings don't start with the path delimiter
if (!flags.is_root && value[0] == '~') {
value = safe_special_char + value.slice(1);
}
// See if the chain needs popping
while (len = chain.length) {
break;
// If the current object at the end of the chain does not
// match the current holder, move one up
// Don't mess with the chain if this is a wrap object
if (!is_wrap && holder !== chain[len-1]) {
last = chain.pop();
// Only pop the path if the popped object isn't a wrapper
if (last && !last.__is_wrap) {
path.pop();
}
case 'number':
// Allow infinite values
if (value && !isFinite(value)) {
if (value > 0) {
value = {dry: '+Infinity'};
} else {
break;
value = {dry: '-Infinity'};
}
}
break;
}
// Has the object been seen before?
seen_path = value_paths.get(value);
return value;
}
}
if (seen_path) {
/**
* Actually replace the object
*
* @author Jelle De Loecker <jelle@develry.be>
* @since 0.1.0
* @version 1.0.11
*/
function replaceObject(dryReplacer, value, chain, flags, is_wrap, holder, path, value_paths, key) {
// If the path is still an array,
// turn it into a string now
if (typeof seen_path != 'string') {
var class_name,
seen_path,
new_value,
replaced,
is_array,
keys,
last,
temp,
len,
i;
// First iterate over the pieces and escape them
for (i = 0; i < seen_path.length; i++) {
if (seen_path[i].indexOf(special_char) > -1) {
seen_path[i] = seen_path[i].replace(special_char_rg, safe_special_char);
}
}
if (typeof value.constructor == 'function') {
class_name = value.constructor.name;
} else {
class_name = 'Object';
}
seen_path = special_char + seen_path.join(special_char);
value_paths.set(value, seen_path);
}
// See if the chain needs popping
while (len = chain.length) {
// Replace the value with the path
new_value = seen_path;
// If the current object at the end of the chain does not
// match the current holder, move one up
// Don't mess with the chain if this is a wrap object
if (!is_wrap && holder !== chain[len-1]) {
// See if the new path is shorter
len = 1;
for (i = 0; i < path.length; i++) {
len += 1 + path[i].length;
}
len += key.length;
last = chain.pop();
if (len < seen_path.length) {
temp = seen_path;
seen_path = path.slice(0);
// Only pop the path if the popped object isn't a wrapper
if (last && !last.__is_wrap) {
path.pop();
}
} else {
break;
}
}
// The key of the current value still needs to be added
seen_path.push(key);
// Has the object been seen before?
seen_path = value_paths.get(value);
// First iterate over the pieces and escape them
for (i = 0; i < seen_path.length; i++) {
if (seen_path[i].indexOf(special_char) > -1) {
seen_path[i] = seen_path[i].replace(special_char_rg, safe_special_char);
}
}
if (seen_path) {
seen_path = special_char + seen_path.join(special_char);
value_paths.set(value, seen_path);
// If the path is still an array,
// turn it into a string now
if (typeof seen_path != 'string') {
// This entry still has to refer to the longer path,
// otherwise it'll refer to itself
seen_path = temp;
}
// First iterate over the pieces and escape them
for (i = 0; i < seen_path.length; i++) {
if (seen_path[i].indexOf(special_char) > -1) {
seen_path[i] = seen_path[i].replace(special_char_rg, safe_special_char);
}
}
value = new_value;
seen_path = special_char + seen_path.join(special_char);
value_paths.set(value, seen_path);
}
break;
}
// Replace the value with the path
new_value = seen_path;
if (!is_root && !is_wrap) {
path.push(key);
} else {
is_root = false;
}
// See if the new path is shorter
len = 1;
for (i = 0; i < path.length; i++) {
len += 1 + path[i].length;
}
len += key.length;
// Make a copy of the current path array
value_paths.set(value, path.slice(0));
if (len < seen_path.length) {
temp = seen_path;
seen_path = path.slice(0);
if (driers[class_name] != null) {
value = driers[class_name].fnc(holder, key, value);
// The key of the current value still needs to be added
seen_path.push(key);
value = {
dry: class_name,
value: value
};
// First iterate over the pieces and escape them
for (i = 0; i < seen_path.length; i++) {
if (seen_path[i].indexOf(special_char) > -1) {
seen_path[i] = seen_path[i].replace(special_char_rg, safe_special_char);
}
}
if (driers[class_name].options.add_path !== false) {
value.drypath = path.slice(0);
}
seen_path = special_char + seen_path.join(special_char);
value_paths.set(value, seen_path);
replaced = {'': value};
} else if (class_name == 'RegExp' && value.constructor == RegExp) {
value = {dry: 'regexp', value: value.toString()};
replaced = {'': value};
} else if (class_name == 'Date' && value.constructor == Date) {
value = {dry: 'date', value: value.toISOString()};
replaced = {'': value};
} else if (typeof value.toDry === 'function') {
temp = value;
value = value.toDry();
// This entry still has to refer to the longer path,
// otherwise it'll refer to itself
seen_path = temp;
}
// If no path was supplied in the toDry,
// get some more class information
if (!value.path) {
if (temp.constructor) {
if (!value.namespace && temp.constructor.namespace) {
value.namespace = temp.constructor.namespace;
}
value = new_value;
if (!value.dry_class) {
value.dry_class = temp.constructor.name;
}
}
}
return value;
}
value.dry = 'toDry';
value.drypath = path.slice(0);
replaced = {'': value};
} else if (typeof value.toJSON === 'function') {
value = value.toJSON();
replaced = {'': value};
} else {
is_array = Array.isArray(value);
}
if (!flags.is_root && !is_wrap) {
path.push(key);
} else {
flags.is_root = false;
}
if (replaced) {
// Push the replaced object on the chain
chain.push(replaced);
// Make a copy of the current path array
value_paths.set(value, path.slice(0));
// Jsonify the replaced object
value = dryReplacer(replaced, false, replaced['']);
if (driers[class_name] != null) {
value = driers[class_name].fnc(holder, key, value);
// At least one part of the path & chain will have
// to be popped off. This is needed for toJSON calls
// that return primitive values
temp = chain.pop();
value = {
dry: class_name,
value: value
};
// Don't pop off anything from the path if the last item
// from the chain was a wrapper for an object,
// because then it'll already be popped of
if (!(temp && temp.__is_wrap && temp.__is_object)) {
temp = path.pop();
}
if (driers[class_name].options.add_path !== false) {
value.drypath = path.slice(0);
}
// Break out of the switch
break;
}
replaced = {'': value};
} else if (class_name === 'RegExp' && value.constructor == RegExp) {
value = {dry: 'regexp', value: value.toString()};
replaced = {'': value};
} else if (class_name === 'Date' && value.constructor == Date) {
// Push this object on the chain
chain.push(value);
// Get numeric value first
temp = value.valueOf();
if (is_array) {
new_value = [];
if (isNaN(temp)) {
temp = 'invalid';
} else {
temp = value.toISOString();
}
for (i = 0; i < value.length; i++) {
new_value[i] = dryReplacer(value, String(i), value[i]);
}
} else {
new_value = {};
keys = Object.keys(value);
value = {dry: 'date', value: temp};
replaced = {'': value};
} else if (typeof value.toDry === 'function') {
temp = value;
value = value.toDry();
for (i = 0; i < keys.length; i++) {
key = keys[i];
new_value[key] = dryReplacer(value, key, value[key]);
}
// If no path was supplied in the toDry,
// get some more class information
if (!value.path) {
if (temp.constructor) {
if (!value.namespace && temp.constructor.namespace) {
value.namespace = temp.constructor.namespace;
}
value = new_value;
if (!value.dry_class) {
value.dry_class = temp.constructor.name;
}
}
}
break;
value.dry = 'toDry';
value.drypath = path.slice(0);
replaced = {'': value};
} else if (typeof value.toJSON === 'function') {
value = value.toJSON();
replaced = {'': value};
} else {
is_array = Array.isArray(value);
}
case 'string':
if (replaced) {
// Push the replaced object on the chain
chain.push(replaced);
// Make sure regular strings don't start with the path delimiter
if (!is_root && value[0] == '~') {
value = safe_special_char + value.slice(1);
}
// Jsonify the replaced object
value = dryReplacer(replaced, false, replaced['']);
break;
// At least one part of the path & chain will have
// to be popped off. This is needed for toJSON calls
// that return primitive values
temp = chain.pop();
case 'number':
// Allow infinite values
if (!isFinite(value)) {
if (value > 0) {
value = {dry: '+Infinity'};
} else {
value = {dry: '-Infinity'};
}
}
break;
// Don't pop off anything from the path if the last item
// from the chain was a wrapper for an object,
// because then it'll already be popped of
if (!(temp && temp.__is_wrap && temp.__is_object)) {
path.pop();
}
// Break out of the switch
return value;
}
// Push this object on the chain
chain.push(value);
if (is_array) {
new_value = [];
for (i = 0; i < value.length; i++) {
new_value[i] = dryReplacer(value, String(i), value[i]);
}
} else {
new_value = recurseGeneralObject(dryReplacer, value);
}
value = new_value;
return value;
}
/**
* Recursively replace the given regular object
*
* @author Jelle De Loecker <jelle@develry.be>
* @since 1.0.11
* @version 1.0.11
*
* @param {Function} dryReplacer
* @param {Object} value
*
* @return {Object}
*/
function recurseGeneralObject(dryReplacer, value) {
var new_value = {},
keys = Object.keys(value),
key,
i;
for (i = 0; i < keys.length; i++) {
key = keys[i];
new_value[key] = dryReplacer(value, key, value[key]);
}
return new_value;
}
/**
* Generate reviver function

@@ -457,2 +503,3 @@ *

temp,
key,
len,

@@ -474,3 +521,4 @@ i;

for (i = 0; i < len; i++) {
entry = obj[keys[i]];
key = keys[i];
entry = obj[key];
entry_type = typeof entry;

@@ -486,3 +534,3 @@

if (wm.has(entry)) {
target[keys[i]] = wm.get(entry);
target[key] = wm.get(entry);
continue;

@@ -495,15 +543,15 @@ }

if (custom_method && entry[custom_method]) {
target[keys[i]] = entry[custom_method].apply(entry, extra_args);
target[key] = entry[custom_method].apply(entry, extra_args);
} else if (driers[name_type] != null) {
// Look for a registered drier function
temp = driers[name_type].fnc(obj, keys[i], entry);
temp = driers[name_type].fnc(obj, key, entry);
if (undriers[name_type]) {
target[keys[i]] = undriers[name_type].fnc(target, keys[i], temp);
target[key] = undriers[name_type].fnc(target, key, temp);
} else {
target[keys[i]] = temp;
target[key] = temp;
}
} else if (entry.dryClone) {
// Look for dryClone after
target[keys[i]] = entry.dryClone(wm, custom_method);
target[key] = entry.dryClone(wm, custom_method);
} else if (entry.toDry) {

@@ -519,9 +567,9 @@ // Perform the toDry function

if (entry.constructor.unDry) {
target[keys[i]] = entry.constructor.unDry(temp, custom_method || true);
target[key] = entry.constructor.unDry(temp, custom_method || true);
} else {
// If there is no undry function, the clone will be a simple object
target[keys[i]] = temp;
target[key] = temp;
}
} else if (name_type == 'Date') {
target[keys[i]] = new Date(entry);
target[key] = new Date(entry);
} else if (name_type == 'RegExp') {

@@ -532,9 +580,9 @@ temp = entry.toString();

if (split) {
target[keys[i]] = RegExp(split[1], split[2]);
target[key] = RegExp(split[1], split[2]);
} else {
target[keys[i]] = RegExp(temp);
target[key] = RegExp(temp);
}
} else if (typeof entry.clone == 'function') {
// If it supplies a clone method, use that
target[keys[i]] = entry.clone();
target[key] = entry.clone();
} else if (entry.toJSON) {

@@ -547,14 +595,14 @@ temp = entry.toJSON();

target[keys[i]] = temp;
target[key] = temp;
} else {
target[keys[i]] = real_clone(entry, custom_method, extra_args, wm);
target[key] = real_clone(entry, custom_method, extra_args, wm);
}
} else {
target[keys[i]] = real_clone(entry, custom_method, extra_args, wm);
target[key] = real_clone(entry, custom_method, extra_args, wm);
}
// Remember this clone for later
wm.set(entry, target[keys[i]]);
wm.set(entry, target[key]);
} else {
target[keys[i]] = entry;
target[key] = entry;
}

@@ -706,2 +754,6 @@ }

if (!constructor) {
console.log('Could not find constructor for', value);
}
return constructor;

@@ -744,3 +796,3 @@ }

* @since 0.1.4
* @version 1.0.8
* @version 1.0.11
*

@@ -751,3 +803,4 @@ * @return {Object}

var temp,
var path,
temp,
key;

@@ -759,6 +812,16 @@

if (!seen.get(current[key])) {
temp = current_path.slice(0);
temp.push(key);
path = current_path.slice(0);
path.push(key);
current[key] = regenerate(root, current, current[key], seen, retrieve, undry_paths, old, temp);
temp = regenerate(root, current, current[key], seen, retrieve, undry_paths, old, path);
// @TODO: Values returned by `unDry` methods also get regenerated,
// even though these could contain properties coming from somewhere else,
// like live HTMLCollections. Assigning anything to that will throw an error.
// This is a workaround to that proble: if the value is exactly the same,
// it's not needed to assign it again, so it won't throw an error,
// but it's not an ideal solution.
if (temp !== current[key]) {
current[key] = temp;
}
}

@@ -1068,3 +1131,3 @@ }

*/
function walk(obj, fnc, result) {
function walk(obj, fnc, result, seen) {

@@ -1077,2 +1140,8 @@ var is_root,

if (!seen) {
seen = new WeakMap();
}
seen.set(obj, true);
if (!result) {

@@ -1094,7 +1163,11 @@ is_root = true;

if (typeof obj[key] == 'object' && obj[key] != null) {
if (Array.isArray(obj[key])) {
result[key] = walk(obj[key], fnc, []);
} else {
result[key] = walk(obj[key], fnc, {});
if (!seen.get(obj[key])) {
if (Array.isArray(obj[key])) {
result[key] = walk(obj[key], fnc, [], seen);
} else {
result[key] = walk(obj[key], fnc, {}, seen);
}
}
result[key] = fnc(key, result[key], result);

@@ -1119,3 +1192,3 @@ } else {

* @since 1.0.0
* @version 1.0.4
* @version 1.0.11
*

@@ -1138,3 +1211,4 @@ * @param {Object} value

key,
old = {};
old = {},
i;

@@ -1190,3 +1264,3 @@ // Create the reviver function

for (var i = 0; i < undry_paths.extra_pass.length; i++) {
for (i = 0; i < undry_paths.extra_pass.length; i++) {
entry = undry_paths.extra_pass[i];

@@ -1193,0 +1267,0 @@ holder = entry[0];

{
"name": "json-dry",
"description": "Don't repeat yourself, JSON: Add support for (circular) references, class instances, ...",
"version": "1.0.10",
"version": "1.0.11",
"author": "Jelle De Loecker <jelle@develry.be>",

@@ -6,0 +6,0 @@ "keywords": [

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc