2020-09-17 23:02:40 +03:00
|
|
|
// Copyright (c) Vitaliy Filippov, 2019+
|
|
|
|
// License: MIT
|
|
|
|
|
2020-09-11 17:49:44 +03:00
|
|
|
function stableStringify(obj, opts)
|
|
|
|
{
|
|
|
|
if (!opts)
|
|
|
|
opts = {};
|
|
|
|
if (typeof opts === 'function')
|
|
|
|
opts = { cmp: opts };
|
|
|
|
let space = opts.space || '';
|
|
|
|
if (typeof space === 'number')
|
|
|
|
space = Array(space+1).join(' ');
|
|
|
|
const cycles = (typeof opts.cycles === 'boolean') ? opts.cycles : false;
|
|
|
|
const cmp = opts.cmp && (function (f)
|
|
|
|
{
|
|
|
|
return function (node)
|
|
|
|
{
|
|
|
|
return function (a, b)
|
|
|
|
{
|
|
|
|
let aobj = { key: a, value: node[a] };
|
|
|
|
let bobj = { key: b, value: node[b] };
|
|
|
|
return f(aobj, bobj);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
})(opts.cmp);
|
|
|
|
const seen = new Map();
|
|
|
|
return (function stringify (parent, key, node, level)
|
|
|
|
{
|
|
|
|
const indent = space ? ('\n' + new Array(level + 1).join(space)) : '';
|
|
|
|
const colonSeparator = space ? ': ' : ':';
|
|
|
|
if (node === undefined)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (typeof node !== 'object' || node === null)
|
|
|
|
{
|
|
|
|
return JSON.stringify(node);
|
|
|
|
}
|
|
|
|
if (node instanceof Array)
|
|
|
|
{
|
|
|
|
const out = [];
|
|
|
|
for (let i = 0; i < node.length; i++)
|
|
|
|
{
|
|
|
|
const item = stringify(node, i, node[i], level+1) || JSON.stringify(null);
|
|
|
|
out.push(indent + space + item);
|
|
|
|
}
|
|
|
|
return '[' + out.join(',') + indent + ']';
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (seen.has(node))
|
|
|
|
{
|
|
|
|
if (cycles)
|
|
|
|
return JSON.stringify('__cycle__');
|
|
|
|
throw new TypeError('Converting circular structure to JSON');
|
|
|
|
}
|
|
|
|
else
|
|
|
|
seen.set(node, true);
|
|
|
|
const keys = Object.keys(node).sort(cmp && cmp(node));
|
|
|
|
const out = [];
|
|
|
|
for (let i = 0; i < keys.length; i++)
|
|
|
|
{
|
|
|
|
const key = keys[i];
|
|
|
|
const value = stringify(node, key, node[key], level+1);
|
|
|
|
if (!value)
|
|
|
|
continue;
|
|
|
|
const keyValue = JSON.stringify(key)
|
|
|
|
+ colonSeparator
|
|
|
|
+ value;
|
|
|
|
out.push(indent + space + keyValue);
|
|
|
|
}
|
|
|
|
seen.delete(node);
|
|
|
|
return '{' + out.join(',') + indent + '}';
|
|
|
|
}
|
|
|
|
})({ '': obj }, '', obj, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = stableStringify;
|