Skip to content

Commit 3dd8d68

Browse files
wraithgarshalvah
andauthored
feat: sort and quote yarn lock keys according to yarn rules (#5751)
Co-authored-by: shalvah <diakon.ng@gmail.com>
1 parent bc5ec05 commit 3dd8d68

File tree

6 files changed

+5406
-5372
lines changed

6 files changed

+5406
-5372
lines changed

workspaces/arborist/lib/yarn-lock.js

+41-13
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@
77
// <key> <value>
88
//
99
// 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.
1312
//
1413
// The data format would support nested objects, but at this time, it
1514
// appears that yarn does not use that for anything, so in the interest
@@ -33,10 +32,44 @@ const consistentResolve = require('./consistent-resolve.js')
3332
const { dirname } = require('path')
3433
const { breadth } = require('treeverse')
3534

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-zA-Z]/g.test(val)
62+
) {
63+
return JSON.stringify(val)
64+
}
65+
66+
return val
67+
}
68+
3669
// sort a key/value object into a string of JSON stringified keys and vals
3770
const sortKV = obj => Object.keys(obj)
3871
.sort(localeCompare)
39-
.map(k => ` ${JSON.stringify(k)} ${JSON.stringify(obj[k])}`)
72+
.map(k => ` ${quoteIfNeeded(k)} ${quoteIfNeeded(obj[k])}`)
4073
.join('\n')
4174

4275
// for checking against previous entries
@@ -171,7 +204,7 @@ class YarnLock {
171204
toString () {
172205
return prefix + [...new Set([...this.entries.values()])]
173206
.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'
175208
}
176209

177210
fromTree (tree) {
@@ -323,19 +356,14 @@ class YarnLockEntry {
323356
// sort objects to the bottom, then alphabetical
324357
return ([...this[_specs]]
325358
.sort(localeCompare)
326-
.map(JSON.stringify).join(', ') +
359+
.map(quoteIfNeeded).join(', ') +
327360
':\n' +
328361
Object.getOwnPropertyNames(this)
329362
.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)
336364
.map(prop =>
337365
typeof this[prop] !== 'object'
338-
? ` ${JSON.stringify(prop)} ${JSON.stringify(this[prop])}\n`
366+
? ` ${prop} ${prop === 'integrity' ? this[prop] : JSON.stringify(this[prop])}\n`
339367
: Object.keys(this[prop]).length === 0 ? ''
340368
: ` ${prop}:\n` + sortKV(this[prop]) + '\n')
341369
.join('')).trim()

workspaces/arborist/tap-snapshots/test/arborist/reify.js.test.cjs

+8-8
Original file line numberDiff line numberDiff line change
@@ -46619,15 +46619,15 @@ exports[`test/arborist/reify.js TAP update a yarn.lock file > updated yarn lock
4661946619
# yarn lockfile v1
4662046620

4662146621

46622-
"abbrev@^1.1.1":
46623-
"integrity" "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
46624-
"resolved" "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz"
46625-
"version" "1.1.1"
46622+
abbrev@^1.1.1:
46623+
version "1.1.1"
46624+
resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz"
46625+
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
4662646626

46627-
"mkdirp@^1.0.2":
46628-
"integrity" "sha512-N2REVrJ/X/jGPfit2d7zea2J1pf7EAR5chIUcfHffAZ7gmlam5U65sAm76+o4ntQbSRdTjYf7qZz3chuHlwXEA=="
46629-
"resolved" "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.2.tgz"
46630-
"version" "1.0.2"
46627+
mkdirp@^1.0.2:
46628+
version "1.0.2"
46629+
resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.2.tgz"
46630+
integrity sha512-N2REVrJ/X/jGPfit2d7zea2J1pf7EAR5chIUcfHffAZ7gmlam5U65sAm76+o4ntQbSRdTjYf7qZz3chuHlwXEA==
4663146631

4663246632
`
4663346633

0 commit comments

Comments
 (0)