@@ -8,82 +8,6 @@ const omit = (object, ...keys) =>
8
8
const omitArray = ( arrayOfObjects , ...keys ) =>
9
9
arrayOfObjects . map ( ( value ) => omit ( value , ...keys ) ) ;
10
10
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
-
87
11
// Function to normalize function/trigger definitions by stripping whitespace from each line
88
12
export const normalizeDefinition = ( definition ) => {
89
13
if ( typeof definition !== 'string' ) {
@@ -304,8 +228,62 @@ const queryDatabaseManifest = async (database) => {
304
228
} ;
305
229
} ;
306
230
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
+ } ) ;
309
287
return found ? autoCompare ( a [ found ] , b [ found ] ) : 0 ;
310
288
} ;
311
289
@@ -327,7 +305,7 @@ const queryDatabaseData = async (database, manifests) => {
327
305
const data = omitArray ( rows , 'value' ) ;
328
306
return [
329
307
table_name ,
330
- data . map ( deepSort ) . sort ( buildSortByKeys ( Object . keys ( data [ 0 ] ?? { } ) ) ) ,
308
+ data . sort ( buildSortByKeys ( Object . keys ( data [ 0 ] ?? { } ) ) ) ,
331
309
] ;
332
310
}
333
311
@@ -346,15 +324,17 @@ const queryDatabaseData = async (database, manifests) => {
346
324
'add_on_sku_id'
347
325
) ;
348
326
349
- return [ table_name , data . map ( deepSort ) . sort ( buildSortByKeys ( Object . keys ( data [ 0 ] ?? { } ) ) ) ] ;
327
+ return [ table_name , data . sort ( buildSortByKeys ( Object . keys ( data [ 0 ] ?? { } ) ) ) ] ;
350
328
} )
351
329
) ;
352
330
353
331
return Object . fromEntries ( result ) ;
354
332
} ;
355
333
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 ) {
358
338
const [ , , database1 , database2 ] = process . argv ;
359
339
360
340
console . log ( 'Compare database manifest between' , database1 , 'and' , database2 ) ;
0 commit comments