1const isObj = val => !!val && !Array.isArray(val) && typeof val === 'object' 2 3const compare = (ak, bk, prefKeys) => 4 prefKeys.includes(ak) && !prefKeys.includes(bk) ? -1 5 : prefKeys.includes(bk) && !prefKeys.includes(ak) ? 1 6 : prefKeys.includes(ak) && prefKeys.includes(bk) 7 ? prefKeys.indexOf(ak) - prefKeys.indexOf(bk) 8 : ak.localeCompare(bk, 'en') 9 10const sort = (replacer, seen) => (key, val) => { 11 const prefKeys = Array.isArray(replacer) ? replacer : [] 12 13 if (typeof replacer === 'function') 14 val = replacer(key, val) 15 16 if (!isObj(val)) 17 return val 18 19 if (seen.has(val)) 20 return seen.get(val) 21 22 const ret = Object.entries(val).sort( 23 ([ak, av], [bk, bv]) => 24 isObj(av) === isObj(bv) ? compare(ak, bk, prefKeys) 25 : isObj(av) ? 1 26 : -1 27 ).reduce((set, [k, v]) => { 28 set[k] = v 29 return set 30 }, {}) 31 32 seen.set(val, ret) 33 return ret 34} 35 36module.exports = (obj, replacer, space = 2) => 37 JSON.stringify(obj, sort(replacer, new Map()), space) 38 + (space ? '\n' : '') 39