UNPKG

4.41 kBJavaScriptView Raw
1import { AutoCleanedStrongCache, cacheSizes, } from "../../utilities/caching/index.js";
2import { registerGlobalCache } from "../caching/getMemoryInternals.js";
3/**
4 * Like JSON.stringify, but with object keys always sorted in the same order.
5 *
6 * To achieve performant sorting, this function uses a Map from JSON-serialized
7 * arrays of keys (in any order) to sorted arrays of the same keys, with a
8 * single sorted array reference shared by all permutations of the keys.
9 *
10 * As a drawback, this function will add a little bit more memory for every
11 * object encountered that has different (more, less, a different order of) keys
12 * than in the past.
13 *
14 * In a typical application, this extra memory usage should not play a
15 * significant role, as `canonicalStringify` will be called for only a limited
16 * number of object shapes, and the cache will not grow beyond a certain point.
17 * But in some edge cases, this could be a problem, so we provide
18 * canonicalStringify.reset() as a way of clearing the cache.
19 * */
20export var canonicalStringify = Object.assign(function canonicalStringify(value) {
21 return JSON.stringify(value, stableObjectReplacer);
22}, {
23 reset: function () {
24 // Clearing the sortingMap will reclaim all cached memory, without
25 // affecting the logical results of canonicalStringify, but potentially
26 // sacrificing performance until the cache is refilled.
27 sortingMap = new AutoCleanedStrongCache(cacheSizes.canonicalStringify || 1000 /* defaultCacheSizes.canonicalStringify */);
28 },
29});
30if (globalThis.__DEV__ !== false) {
31 registerGlobalCache("canonicalStringify", function () { return sortingMap.size; });
32}
33// Values are JSON-serialized arrays of object keys (in any order), and values
34// are sorted arrays of the same keys.
35var sortingMap;
36canonicalStringify.reset();
37// The JSON.stringify function takes an optional second argument called a
38// replacer function. This function is called for each key-value pair in the
39// object being stringified, and its return value is used instead of the
40// original value. If the replacer function returns a new value, that value is
41// stringified as JSON instead of the original value of the property.
42// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#the_replacer_parameter
43function stableObjectReplacer(key, value) {
44 if (value && typeof value === "object") {
45 var proto = Object.getPrototypeOf(value);
46 // We don't want to mess with objects that are not "plain" objects, which
47 // means their prototype is either Object.prototype or null. This check also
48 // prevents needlessly rearranging the indices of arrays.
49 if (proto === Object.prototype || proto === null) {
50 var keys = Object.keys(value);
51 // If keys is already sorted, let JSON.stringify serialize the original
52 // value instead of creating a new object with keys in the same order.
53 if (keys.every(everyKeyInOrder))
54 return value;
55 var unsortedKey = JSON.stringify(keys);
56 var sortedKeys = sortingMap.get(unsortedKey);
57 if (!sortedKeys) {
58 keys.sort();
59 var sortedKey = JSON.stringify(keys);
60 // Checking for sortedKey in the sortingMap allows us to share the same
61 // sorted array reference for all permutations of the same set of keys.
62 sortedKeys = sortingMap.get(sortedKey) || keys;
63 sortingMap.set(unsortedKey, sortedKeys);
64 sortingMap.set(sortedKey, sortedKeys);
65 }
66 var sortedObject_1 = Object.create(proto);
67 // Reassigning the keys in sorted order will cause JSON.stringify to
68 // serialize them in sorted order.
69 sortedKeys.forEach(function (key) {
70 sortedObject_1[key] = value[key];
71 });
72 return sortedObject_1;
73 }
74 }
75 return value;
76}
77// Since everything that happens in stableObjectReplacer benefits from being as
78// efficient as possible, we use a static function as the callback for
79// keys.every in order to test if the provided keys are already sorted without
80// allocating extra memory for a callback.
81function everyKeyInOrder(key, i, keys) {
82 return i === 0 || keys[i - 1] <= key;
83}
84//# sourceMappingURL=canonicalStringify.js.map
\No newline at end of file