1 | import { AutoCleanedStrongCache, cacheSizes, } from "../../utilities/caching/index.js";
|
2 | import { 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 | * */
|
20 | export 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 | });
|
30 | if (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.
|
35 | var sortingMap;
|
36 | canonicalStringify.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
|
43 | function 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.
|
81 | function everyKeyInOrder(key, i, keys) {
|
82 | return i === 0 || keys[i - 1] <= key;
|
83 | }
|
84 | //# sourceMappingURL=canonicalStringify.js.map |
\ | No newline at end of file |