You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
84 lines
4.3 KiB
84 lines
4.3 KiB
4 months ago
|
import { AutoCleanedStrongCache, cacheSizes, } from "../../utilities/caching/index.js";
|
||
|
import { registerGlobalCache } from "../caching/getMemoryInternals.js";
|
||
|
/**
|
||
|
* Like JSON.stringify, but with object keys always sorted in the same order.
|
||
|
*
|
||
|
* To achieve performant sorting, this function uses a Map from JSON-serialized
|
||
|
* arrays of keys (in any order) to sorted arrays of the same keys, with a
|
||
|
* single sorted array reference shared by all permutations of the keys.
|
||
|
*
|
||
|
* As a drawback, this function will add a little bit more memory for every
|
||
|
* object encountered that has different (more, less, a different order of) keys
|
||
|
* than in the past.
|
||
|
*
|
||
|
* In a typical application, this extra memory usage should not play a
|
||
|
* significant role, as `canonicalStringify` will be called for only a limited
|
||
|
* number of object shapes, and the cache will not grow beyond a certain point.
|
||
|
* But in some edge cases, this could be a problem, so we provide
|
||
|
* canonicalStringify.reset() as a way of clearing the cache.
|
||
|
* */
|
||
|
export var canonicalStringify = Object.assign(function canonicalStringify(value) {
|
||
|
return JSON.stringify(value, stableObjectReplacer);
|
||
|
}, {
|
||
|
reset: function () {
|
||
|
// Clearing the sortingMap will reclaim all cached memory, without
|
||
|
// affecting the logical results of canonicalStringify, but potentially
|
||
|
// sacrificing performance until the cache is refilled.
|
||
|
sortingMap = new AutoCleanedStrongCache(cacheSizes.canonicalStringify || 1000 /* defaultCacheSizes.canonicalStringify */);
|
||
|
},
|
||
|
});
|
||
|
if (globalThis.__DEV__ !== false) {
|
||
|
registerGlobalCache("canonicalStringify", function () { return sortingMap.size; });
|
||
|
}
|
||
|
// Values are JSON-serialized arrays of object keys (in any order), and values
|
||
|
// are sorted arrays of the same keys.
|
||
|
var sortingMap;
|
||
|
canonicalStringify.reset();
|
||
|
// The JSON.stringify function takes an optional second argument called a
|
||
|
// replacer function. This function is called for each key-value pair in the
|
||
|
// object being stringified, and its return value is used instead of the
|
||
|
// original value. If the replacer function returns a new value, that value is
|
||
|
// stringified as JSON instead of the original value of the property.
|
||
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#the_replacer_parameter
|
||
|
function stableObjectReplacer(key, value) {
|
||
|
if (value && typeof value === "object") {
|
||
|
var proto = Object.getPrototypeOf(value);
|
||
|
// We don't want to mess with objects that are not "plain" objects, which
|
||
|
// means their prototype is either Object.prototype or null. This check also
|
||
|
// prevents needlessly rearranging the indices of arrays.
|
||
|
if (proto === Object.prototype || proto === null) {
|
||
|
var keys = Object.keys(value);
|
||
|
// If keys is already sorted, let JSON.stringify serialize the original
|
||
|
// value instead of creating a new object with keys in the same order.
|
||
|
if (keys.every(everyKeyInOrder))
|
||
|
return value;
|
||
|
var unsortedKey = JSON.stringify(keys);
|
||
|
var sortedKeys = sortingMap.get(unsortedKey);
|
||
|
if (!sortedKeys) {
|
||
|
keys.sort();
|
||
|
var sortedKey = JSON.stringify(keys);
|
||
|
// Checking for sortedKey in the sortingMap allows us to share the same
|
||
|
// sorted array reference for all permutations of the same set of keys.
|
||
|
sortedKeys = sortingMap.get(sortedKey) || keys;
|
||
|
sortingMap.set(unsortedKey, sortedKeys);
|
||
|
sortingMap.set(sortedKey, sortedKeys);
|
||
|
}
|
||
|
var sortedObject_1 = Object.create(proto);
|
||
|
// Reassigning the keys in sorted order will cause JSON.stringify to
|
||
|
// serialize them in sorted order.
|
||
|
sortedKeys.forEach(function (key) {
|
||
|
sortedObject_1[key] = value[key];
|
||
|
});
|
||
|
return sortedObject_1;
|
||
|
}
|
||
|
}
|
||
|
return value;
|
||
|
}
|
||
|
// Since everything that happens in stableObjectReplacer benefits from being as
|
||
|
// efficient as possible, we use a static function as the callback for
|
||
|
// keys.every in order to test if the provided keys are already sorted without
|
||
|
// allocating extra memory for a callback.
|
||
|
function everyKeyInOrder(key, i, keys) {
|
||
|
return i === 0 || keys[i - 1] <= key;
|
||
|
}
|
||
|
//# sourceMappingURL=canonicalStringify.js.map
|