7
7
// <key> <value>
8
8
//
9
9
// Assume that any key or value might be quoted, though that's only done
10
- // in practice if certain chars are in the string. Quoting unnecessarily
11
- // does not cause problems for yarn, so that's what we do when we write
12
- // it back.
10
+ // in practice if certain chars are in the string. When writing back, we follow
11
+ // Yarn's rules for quoting, to cause minimal friction.
13
12
//
14
13
// The data format would support nested objects, but at this time, it
15
14
// appears that yarn does not use that for anything, so in the interest
@@ -33,10 +32,44 @@ const consistentResolve = require('./consistent-resolve.js')
33
32
const { dirname } = require ( 'path' )
34
33
const { breadth } = require ( 'treeverse' )
35
34
35
+ // Sort Yarn entries respecting the yarn.lock sort order
36
+ const yarnEntryPriorities = {
37
+ name : 1 ,
38
+ version : 2 ,
39
+ uid : 3 ,
40
+ resolved : 4 ,
41
+ integrity : 5 ,
42
+ registry : 6 ,
43
+ dependencies : 7 ,
44
+ }
45
+
46
+ const priorityThenLocaleCompare = ( a , b ) => {
47
+ if ( ! yarnEntryPriorities [ a ] && ! yarnEntryPriorities [ b ] ) {
48
+ return localeCompare ( a , b )
49
+ }
50
+ /* istanbul ignore next */
51
+ return ( yarnEntryPriorities [ a ] || 100 ) > ( yarnEntryPriorities [ b ] || 100 ) ? 1 : - 1
52
+ }
53
+
54
+ const quoteIfNeeded = val => {
55
+ if (
56
+ typeof val === 'boolean' ||
57
+ typeof val === 'number' ||
58
+ val . startsWith ( 'true' ) ||
59
+ val . startsWith ( 'false' ) ||
60
+ / [: \s \n \\ " , [ \] ] / g. test ( val ) ||
61
+ ! / ^ [ a - z A - Z ] / g. test ( val )
62
+ ) {
63
+ return JSON . stringify ( val )
64
+ }
65
+
66
+ return val
67
+ }
68
+
36
69
// sort a key/value object into a string of JSON stringified keys and vals
37
70
const sortKV = obj => Object . keys ( obj )
38
71
. sort ( localeCompare )
39
- . map ( k => ` ${ JSON . stringify ( k ) } ${ JSON . stringify ( obj [ k ] ) } ` )
72
+ . map ( k => ` ${ quoteIfNeeded ( k ) } ${ quoteIfNeeded ( obj [ k ] ) } ` )
40
73
. join ( '\n' )
41
74
42
75
// for checking against previous entries
@@ -171,7 +204,7 @@ class YarnLock {
171
204
toString ( ) {
172
205
return prefix + [ ...new Set ( [ ...this . entries . values ( ) ] ) ]
173
206
. map ( e => e . toString ( ) )
174
- . sort ( localeCompare ) . join ( '\n\n' ) + '\n'
207
+ . sort ( ( a , b ) => localeCompare ( a . replace ( / " / g , '' ) , b . replace ( / " / g , '' ) ) ) . join ( '\n\n' ) + '\n'
175
208
}
176
209
177
210
fromTree ( tree ) {
@@ -323,19 +356,14 @@ class YarnLockEntry {
323
356
// sort objects to the bottom, then alphabetical
324
357
return ( [ ...this [ _specs ] ]
325
358
. sort ( localeCompare )
326
- . map ( JSON . stringify ) . join ( ', ' ) +
359
+ . map ( quoteIfNeeded ) . join ( ', ' ) +
327
360
':\n' +
328
361
Object . getOwnPropertyNames ( this )
329
362
. filter ( prop => this [ prop ] !== null )
330
- . sort (
331
- ( a , b ) =>
332
- /* istanbul ignore next - sort call order is unpredictable */
333
- ( typeof this [ a ] === 'object' ) === ( typeof this [ b ] === 'object' )
334
- ? localeCompare ( a , b )
335
- : typeof this [ a ] === 'object' ? 1 : - 1 )
363
+ . sort ( priorityThenLocaleCompare )
336
364
. map ( prop =>
337
365
typeof this [ prop ] !== 'object'
338
- ? ` ${ JSON . stringify ( prop ) } ${ JSON . stringify ( this [ prop ] ) } \n`
366
+ ? ` ${ prop } ${ prop === 'integrity' ? this [ prop ] : JSON . stringify ( this [ prop ] ) } \n`
339
367
: Object . keys ( this [ prop ] ) . length === 0 ? ''
340
368
: ` ${ prop } :\n` + sortKV ( this [ prop ] ) + '\n' )
341
369
. join ( '' ) ) . trim ( )
0 commit comments