Skip to content

Commit 1bb23f3

Browse files
committed
refactor: only ensure order of keys when comparing objects
1 parent b2eb690 commit 1bb23f3

File tree

2 files changed

+237
-189
lines changed

2 files changed

+237
-189
lines changed

.scripts/compare-database.js

Lines changed: 62 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -8,82 +8,6 @@ const omit = (object, ...keys) =>
88
const omitArray = (arrayOfObjects, ...keys) =>
99
arrayOfObjects.map((value) => omit(value, ...keys));
1010

11-
// Exported utility functions for testing
12-
export const autoCompare = (a, b) => {
13-
// Handle null values first
14-
if (a === null && b === null) return 0;
15-
if (a === null) return -1; // null comes before other values
16-
if (b === null) return 1;
17-
18-
// Handle undefined values
19-
if (a === undefined && b === undefined) return 0;
20-
if (a === undefined) return -1; // undefined comes before other values
21-
if (b === undefined) return 1;
22-
23-
// Compare types
24-
if (typeof a !== typeof b) {
25-
return (typeof a).localeCompare(typeof b);
26-
}
27-
28-
// Handle arrays
29-
if (Array.isArray(a) && Array.isArray(b)) {
30-
for (let i = 0; i < Math.min(a.length, b.length); i++) {
31-
const comparison = autoCompare(a[i], b[i]);
32-
if (comparison !== 0) {
33-
return comparison;
34-
}
35-
}
36-
return a.length - b.length;
37-
}
38-
39-
// Handle objects (but not arrays)
40-
if (typeof a === 'object' && !Array.isArray(a)) {
41-
const aKeys = Object.keys(a).sort();
42-
const bKeys = Object.keys(b).sort();
43-
44-
for (let i = 0; i < Math.min(aKeys.length, bKeys.length); i++) {
45-
if (aKeys[i] !== bKeys[i]) {
46-
return aKeys[i].localeCompare(bKeys[i]);
47-
}
48-
const comparison = autoCompare(a[aKeys[i]], b[bKeys[i]]);
49-
if (comparison !== 0) {
50-
return comparison;
51-
}
52-
}
53-
54-
return aKeys.length - bKeys.length;
55-
}
56-
57-
// Handle numbers
58-
if (typeof a === 'number' && typeof b === 'number') {
59-
return a - b;
60-
}
61-
62-
// Handle booleans
63-
if (typeof a === 'boolean' && typeof b === 'boolean') {
64-
return a === b ? 0 : (a ? 1 : -1); // false < true
65-
}
66-
67-
// Handle strings and other primitives
68-
return String(a).localeCompare(String(b));
69-
};
70-
71-
export const deepSort = (obj) => {
72-
if (Array.isArray(obj)) {
73-
return obj.map(deepSort).sort(autoCompare);
74-
}
75-
76-
if (obj && typeof obj === 'object') {
77-
const sorted = {};
78-
Object.keys(obj).sort().forEach(key => {
79-
sorted[key] = deepSort(obj[key]);
80-
});
81-
return sorted;
82-
}
83-
84-
return obj;
85-
};
86-
8711
// Function to normalize function/trigger definitions by stripping whitespace from each line
8812
export const normalizeDefinition = (definition) => {
8913
if (typeof definition !== 'string') {
@@ -304,8 +228,62 @@ const queryDatabaseManifest = async (database) => {
304228
};
305229
};
306230

307-
const buildSortByKeys = (keys) => (a, b) => {
308-
const found = keys.find((key) => a[key] !== b[key]);
231+
// Export utility functions first
232+
export const autoCompare = (a, b) => {
233+
if (typeof a !== typeof b) {
234+
return (typeof a).localeCompare(typeof b);
235+
}
236+
237+
if (typeof a === 'object' && a !== null && b !== null) {
238+
const aKeys = Object.keys(a).sort();
239+
const bKeys = Object.keys(b).sort();
240+
241+
for (let i = 0; i < Math.min(aKeys.length, bKeys.length); i++) {
242+
if (aKeys[i] !== bKeys[i]) {
243+
return aKeys[i].localeCompare(bKeys[i]);
244+
}
245+
const comparison = autoCompare(a[aKeys[i]], b[bKeys[i]]);
246+
if (comparison !== 0) {
247+
return comparison;
248+
}
249+
}
250+
251+
return aKeys.length - bKeys.length;
252+
}
253+
254+
return String(a).localeCompare(String(b));
255+
};
256+
257+
// Function to evaluate the complexity of a value
258+
// Higher values indicate more complex types that should be compared first
259+
export const getValueComplexity = (value) => {
260+
if (value === null || value === undefined) return 0;
261+
if (typeof value === 'boolean') return 1;
262+
if (typeof value === 'number') return 2;
263+
if (typeof value === 'string') return 3;
264+
if (Array.isArray(value)) return 4;
265+
if (typeof value === 'object') return 5;
266+
return 0;
267+
};
268+
269+
export const buildSortByKeys = (keys) => (a, b) => {
270+
// Sort keys based on value complexity and then find the first differing key
271+
const sortedKeys = keys.slice().sort((keyA, keyB) => {
272+
const complexityA = Math.max(getValueComplexity(a[keyA]), getValueComplexity(b[keyA]));
273+
const complexityB = Math.max(getValueComplexity(a[keyB]), getValueComplexity(b[keyB]));
274+
275+
// Sort by complexity descending (more complex first), then by key name ascending
276+
if (complexityA !== complexityB) {
277+
return complexityB - complexityA;
278+
}
279+
return keyA.localeCompare(keyB);
280+
});
281+
282+
// Use deep comparison instead of reference comparison for objects
283+
const found = sortedKeys.find((key) => {
284+
const comparison = autoCompare(a[key], b[key]);
285+
return comparison !== 0;
286+
});
309287
return found ? autoCompare(a[found], b[found]) : 0;
310288
};
311289

@@ -327,7 +305,7 @@ const queryDatabaseData = async (database, manifests) => {
327305
const data = omitArray(rows, 'value');
328306
return [
329307
table_name,
330-
data.map(deepSort).sort(buildSortByKeys(Object.keys(data[0] ?? {}))),
308+
data.sort(buildSortByKeys(Object.keys(data[0] ?? {}))),
331309
];
332310
}
333311

@@ -346,15 +324,17 @@ const queryDatabaseData = async (database, manifests) => {
346324
'add_on_sku_id'
347325
);
348326

349-
return [table_name, data.map(deepSort).sort(buildSortByKeys(Object.keys(data[0] ?? {})))];
327+
return [table_name, data.sort(buildSortByKeys(Object.keys(data[0] ?? {})))];
350328
})
351329
);
352330

353331
return Object.fromEntries(result);
354332
};
355333

356-
// Only run the main logic when this file is executed directly
357-
if (import.meta.url === `file://${process.argv[1]}`) {
334+
// Main execution logic - only runs when file is executed directly
335+
const isMainModule = import.meta.url === new URL(process.argv[1], 'file://').href;
336+
337+
if (isMainModule) {
358338
const [, , database1, database2] = process.argv;
359339

360340
console.log('Compare database manifest between', database1, 'and', database2);

0 commit comments

Comments
 (0)