@npmcli/arborist 4.0.5 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/lib/arborist/build-ideal-tree.js +6 -0
- package/lib/arborist/load-actual.js +21 -3
- package/lib/arborist/load-virtual.js +1 -0
- package/lib/edge.js +38 -3
- package/lib/node.js +45 -0
- package/lib/override-set.js +123 -0
- package/lib/place-dep.js +1 -0
- package/lib/printable.js +11 -2
- package/lib/shrinkwrap.js +15 -4
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -4,8 +4,8 @@ Inspect and manage `node_modules` trees.
|
|
|
4
4
|
|
|
5
5
|

|
|
6
6
|
|
|
7
|
-
There's more documentation [in the
|
|
8
|
-
folder](https://github.com/npm/arborist/tree/main/
|
|
7
|
+
There's more documentation [in the docs
|
|
8
|
+
folder](https://github.com/npm/arborist/tree/main/docs).
|
|
9
9
|
|
|
10
10
|
## USAGE
|
|
11
11
|
|
|
@@ -379,6 +379,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
379
379
|
optional: false,
|
|
380
380
|
global: this[_global],
|
|
381
381
|
legacyPeerDeps: this.legacyPeerDeps,
|
|
382
|
+
loadOverrides: true,
|
|
382
383
|
})
|
|
383
384
|
if (root.isLink) {
|
|
384
385
|
root.target = new Node({
|
|
@@ -676,6 +677,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
676
677
|
// calls rather than walking over everything in the tree.
|
|
677
678
|
const set = this.idealTree.inventory
|
|
678
679
|
.filter(n => this[_shouldUpdateNode](n))
|
|
680
|
+
// XXX add any invalid edgesOut to the queue
|
|
679
681
|
for (const node of set) {
|
|
680
682
|
for (const edge of node.edgesIn) {
|
|
681
683
|
this.addTracker('idealTree', edge.from.name, edge.from.location)
|
|
@@ -772,7 +774,10 @@ This is a one-time fix-up, please be patient...
|
|
|
772
774
|
[_buildDeps] () {
|
|
773
775
|
process.emit('time', 'idealTree:buildDeps')
|
|
774
776
|
const tree = this.idealTree.target
|
|
777
|
+
tree.assertRootOverrides()
|
|
775
778
|
this[_depsQueue].push(tree)
|
|
779
|
+
// XXX also push anything that depends on a node with a name
|
|
780
|
+
// in the override list
|
|
776
781
|
this.log.silly('idealTree', 'buildDeps')
|
|
777
782
|
this.addTracker('idealTree', tree.name, '')
|
|
778
783
|
return this[_buildDepStep]()
|
|
@@ -1112,6 +1117,7 @@ This is a one-time fix-up, please be patient...
|
|
|
1112
1117
|
path: node.realpath,
|
|
1113
1118
|
sourceReference: node,
|
|
1114
1119
|
legacyPeerDeps: this.legacyPeerDeps,
|
|
1120
|
+
overrides: node.overrides,
|
|
1115
1121
|
})
|
|
1116
1122
|
|
|
1117
1123
|
// also need to set up any targets from any link deps, so that
|
|
@@ -127,6 +127,7 @@ module.exports = cls => class ActualLoader extends cls {
|
|
|
127
127
|
realpath: real,
|
|
128
128
|
pkg: {},
|
|
129
129
|
global,
|
|
130
|
+
loadOverrides: true,
|
|
130
131
|
})
|
|
131
132
|
return this[_loadActualActually]({ root, ignoreMissing, global })
|
|
132
133
|
}
|
|
@@ -135,8 +136,11 @@ module.exports = cls => class ActualLoader extends cls {
|
|
|
135
136
|
this[_actualTree] = await this[_loadFSNode]({
|
|
136
137
|
path: this.path,
|
|
137
138
|
real: await realpath(this.path, this[_rpcache], this[_stcache]),
|
|
139
|
+
loadOverrides: true,
|
|
138
140
|
})
|
|
139
141
|
|
|
142
|
+
this[_actualTree].assertRootOverrides()
|
|
143
|
+
|
|
140
144
|
// Note: hidden lockfile will be rejected if it's not the latest thing
|
|
141
145
|
// in the folder, or if any of the entries in the hidden lockfile are
|
|
142
146
|
// missing.
|
|
@@ -236,13 +240,26 @@ module.exports = cls => class ActualLoader extends cls {
|
|
|
236
240
|
this[_actualTree] = root
|
|
237
241
|
}
|
|
238
242
|
|
|
239
|
-
[_loadFSNode] ({ path, parent, real, root }) {
|
|
243
|
+
[_loadFSNode] ({ path, parent, real, root, loadOverrides }) {
|
|
240
244
|
if (!real) {
|
|
241
245
|
return realpath(path, this[_rpcache], this[_stcache])
|
|
242
246
|
.then(
|
|
243
|
-
real => this[_loadFSNode]({
|
|
247
|
+
real => this[_loadFSNode]({
|
|
248
|
+
path,
|
|
249
|
+
parent,
|
|
250
|
+
real,
|
|
251
|
+
root,
|
|
252
|
+
loadOverrides,
|
|
253
|
+
}),
|
|
244
254
|
// if realpath fails, just provide a dummy error node
|
|
245
|
-
error => new Node({
|
|
255
|
+
error => new Node({
|
|
256
|
+
error,
|
|
257
|
+
path,
|
|
258
|
+
realpath: path,
|
|
259
|
+
parent,
|
|
260
|
+
root,
|
|
261
|
+
loadOverrides,
|
|
262
|
+
})
|
|
246
263
|
)
|
|
247
264
|
}
|
|
248
265
|
|
|
@@ -271,6 +288,7 @@ module.exports = cls => class ActualLoader extends cls {
|
|
|
271
288
|
error,
|
|
272
289
|
parent,
|
|
273
290
|
root,
|
|
291
|
+
loadOverrides,
|
|
274
292
|
})
|
|
275
293
|
})
|
|
276
294
|
.then(node => {
|
package/lib/edge.js
CHANGED
|
@@ -29,6 +29,7 @@ class ArboristEdge {}
|
|
|
29
29
|
const printableEdge = (edge) => {
|
|
30
30
|
const edgeFrom = edge.from && edge.from.location
|
|
31
31
|
const edgeTo = edge.to && edge.to.location
|
|
32
|
+
const override = edge.overrides && edge.overrides.value
|
|
32
33
|
|
|
33
34
|
return Object.assign(new ArboristEdge(), {
|
|
34
35
|
name: edge.name,
|
|
@@ -38,12 +39,13 @@ const printableEdge = (edge) => {
|
|
|
38
39
|
...(edgeTo ? { to: edgeTo } : {}),
|
|
39
40
|
...(edge.error ? { error: edge.error } : {}),
|
|
40
41
|
...(edge.peerConflicted ? { peerConflicted: true } : {}),
|
|
42
|
+
...(override ? { overridden: override } : {}),
|
|
41
43
|
})
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
class Edge {
|
|
45
47
|
constructor (options) {
|
|
46
|
-
const { type, name, spec, accept, from } = options
|
|
48
|
+
const { type, name, spec, accept, from, overrides } = options
|
|
47
49
|
|
|
48
50
|
if (typeof spec !== 'string') {
|
|
49
51
|
throw new TypeError('must provide string spec')
|
|
@@ -55,6 +57,10 @@ class Edge {
|
|
|
55
57
|
|
|
56
58
|
this[_spec] = spec
|
|
57
59
|
|
|
60
|
+
if (overrides !== undefined) {
|
|
61
|
+
this.overrides = overrides
|
|
62
|
+
}
|
|
63
|
+
|
|
58
64
|
if (accept !== undefined) {
|
|
59
65
|
if (typeof accept !== 'string') {
|
|
60
66
|
throw new TypeError('accept field must be a string if provided')
|
|
@@ -82,8 +88,11 @@ class Edge {
|
|
|
82
88
|
}
|
|
83
89
|
|
|
84
90
|
satisfiedBy (node) {
|
|
85
|
-
|
|
86
|
-
|
|
91
|
+
if (node.name !== this.name) {
|
|
92
|
+
return false
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return depValid(node, this.spec, this.accept, this.from)
|
|
87
96
|
}
|
|
88
97
|
|
|
89
98
|
explain (seen = []) {
|
|
@@ -101,6 +110,10 @@ class Edge {
|
|
|
101
110
|
type: this.type,
|
|
102
111
|
name: this.name,
|
|
103
112
|
spec: this.spec,
|
|
113
|
+
...(this.rawSpec !== this.spec ? {
|
|
114
|
+
rawSpec: this.rawSpec,
|
|
115
|
+
overridden: true,
|
|
116
|
+
} : {}),
|
|
104
117
|
...(bundled ? { bundled } : {}),
|
|
105
118
|
...(error ? { error } : {}),
|
|
106
119
|
...(from ? { from: from.explain(null, seen) } : {}),
|
|
@@ -143,7 +156,28 @@ class Edge {
|
|
|
143
156
|
return this[_name]
|
|
144
157
|
}
|
|
145
158
|
|
|
159
|
+
get rawSpec () {
|
|
160
|
+
return this[_spec]
|
|
161
|
+
}
|
|
162
|
+
|
|
146
163
|
get spec () {
|
|
164
|
+
if (this.overrides && this.overrides.value && this.overrides.name === this.name) {
|
|
165
|
+
if (this.overrides.value.startsWith('$')) {
|
|
166
|
+
const ref = this.overrides.value.slice(1)
|
|
167
|
+
const pkg = this.from.root.package
|
|
168
|
+
const overrideSpec = (pkg.devDependencies && pkg.devDependencies[ref]) ||
|
|
169
|
+
(pkg.optionalDependencies && pkg.optionalDependencies[ref]) ||
|
|
170
|
+
(pkg.dependencies && pkg.dependencies[ref]) ||
|
|
171
|
+
(pkg.peerDependencies && pkg.peerDependencies[ref])
|
|
172
|
+
|
|
173
|
+
if (overrideSpec) {
|
|
174
|
+
return overrideSpec
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
throw new Error(`Unable to resolve reference ${this.overrides.value}`)
|
|
178
|
+
}
|
|
179
|
+
return this.overrides.value
|
|
180
|
+
}
|
|
147
181
|
return this[_spec]
|
|
148
182
|
}
|
|
149
183
|
|
|
@@ -213,6 +247,7 @@ class Edge {
|
|
|
213
247
|
if (node.edgesOut.has(this.name)) {
|
|
214
248
|
node.edgesOut.get(this.name).detach()
|
|
215
249
|
}
|
|
250
|
+
|
|
216
251
|
node.addEdgeOut(this)
|
|
217
252
|
this.reload()
|
|
218
253
|
}
|
package/lib/node.js
CHANGED
|
@@ -32,6 +32,7 @@ const semver = require('semver')
|
|
|
32
32
|
const nameFromFolder = require('@npmcli/name-from-folder')
|
|
33
33
|
const Edge = require('./edge.js')
|
|
34
34
|
const Inventory = require('./inventory.js')
|
|
35
|
+
const OverrideSet = require('./override-set.js')
|
|
35
36
|
const { normalize } = require('read-package-json-fast')
|
|
36
37
|
const { getPaths: getBinPaths } = require('bin-links')
|
|
37
38
|
const npa = require('npm-package-arg')
|
|
@@ -88,6 +89,8 @@ class Node {
|
|
|
88
89
|
legacyPeerDeps = false,
|
|
89
90
|
linksIn,
|
|
90
91
|
hasShrinkwrap,
|
|
92
|
+
overrides,
|
|
93
|
+
loadOverrides = false,
|
|
91
94
|
extraneous = true,
|
|
92
95
|
dev = true,
|
|
93
96
|
optional = true,
|
|
@@ -190,6 +193,17 @@ class Node {
|
|
|
190
193
|
// because this.package is read when adding to inventory
|
|
191
194
|
this[_package] = pkg && typeof pkg === 'object' ? pkg : {}
|
|
192
195
|
|
|
196
|
+
if (overrides) {
|
|
197
|
+
this.overrides = overrides
|
|
198
|
+
} else if (loadOverrides) {
|
|
199
|
+
const overrides = this[_package].overrides || {}
|
|
200
|
+
if (Object.keys(overrides).length > 0) {
|
|
201
|
+
this.overrides = new OverrideSet({
|
|
202
|
+
overrides: this[_package].overrides,
|
|
203
|
+
})
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
193
207
|
// only relevant for the root and top nodes
|
|
194
208
|
this.meta = meta
|
|
195
209
|
|
|
@@ -963,6 +977,11 @@ class Node {
|
|
|
963
977
|
return false
|
|
964
978
|
}
|
|
965
979
|
|
|
980
|
+
// XXX need to check for two root nodes?
|
|
981
|
+
if (node.overrides !== this.overrides) {
|
|
982
|
+
return false
|
|
983
|
+
}
|
|
984
|
+
|
|
966
985
|
ignorePeers = new Set(ignorePeers)
|
|
967
986
|
|
|
968
987
|
// gather up all the deps of this node and that are only depended
|
|
@@ -1208,6 +1227,10 @@ class Node {
|
|
|
1208
1227
|
this[_changePath](newPath)
|
|
1209
1228
|
}
|
|
1210
1229
|
|
|
1230
|
+
if (parent.overrides) {
|
|
1231
|
+
this.overrides = parent.overrides.getNodeRule(this)
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1211
1234
|
// clobbers anything at that path, resets all appropriate references
|
|
1212
1235
|
this.root = parent.root
|
|
1213
1236
|
}
|
|
@@ -1279,11 +1302,33 @@ class Node {
|
|
|
1279
1302
|
}
|
|
1280
1303
|
}
|
|
1281
1304
|
|
|
1305
|
+
assertRootOverrides () {
|
|
1306
|
+
if (!this.isProjectRoot || !this.overrides) {
|
|
1307
|
+
return
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
for (const edge of this.edgesOut.values()) {
|
|
1311
|
+
// if these differ an override has been applied, those are not allowed
|
|
1312
|
+
// for top level dependencies so throw an error
|
|
1313
|
+
if (edge.spec !== edge.rawSpec && !edge.spec.startsWith('$')) {
|
|
1314
|
+
throw Object.assign(new Error(`Override for ${edge.name}@${edge.rawSpec} conflicts with direct dependency`), { code: 'EOVERRIDE' })
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1282
1319
|
addEdgeOut (edge) {
|
|
1320
|
+
if (this.overrides) {
|
|
1321
|
+
edge.overrides = this.overrides.getEdgeRule(edge)
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1283
1324
|
this.edgesOut.set(edge.name, edge)
|
|
1284
1325
|
}
|
|
1285
1326
|
|
|
1286
1327
|
addEdgeIn (edge) {
|
|
1328
|
+
if (edge.overrides) {
|
|
1329
|
+
this.overrides = edge.overrides
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1287
1332
|
this.edgesIn.add(edge)
|
|
1288
1333
|
|
|
1289
1334
|
// try to get metadata from the yarn.lock file
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
const npa = require('npm-package-arg')
|
|
2
|
+
const semver = require('semver')
|
|
3
|
+
|
|
4
|
+
class OverrideSet {
|
|
5
|
+
constructor ({ overrides, key, parent }) {
|
|
6
|
+
this.parent = parent
|
|
7
|
+
this.children = new Map()
|
|
8
|
+
|
|
9
|
+
if (typeof overrides === 'string') {
|
|
10
|
+
overrides = { '.': overrides }
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// change a literal empty string to * so we can use truthiness checks on
|
|
14
|
+
// the value property later
|
|
15
|
+
if (overrides['.'] === '') {
|
|
16
|
+
overrides['.'] = '*'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (parent) {
|
|
20
|
+
const spec = npa(key)
|
|
21
|
+
if (!spec.name) {
|
|
22
|
+
throw new Error(`Override without name: ${key}`)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
this.name = spec.name
|
|
26
|
+
spec.name = ''
|
|
27
|
+
this.key = key
|
|
28
|
+
this.keySpec = spec.rawSpec === '' ? '' : spec.toString()
|
|
29
|
+
this.value = overrides['.'] || this.keySpec
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
for (const [key, childOverrides] of Object.entries(overrides)) {
|
|
33
|
+
if (key === '.') {
|
|
34
|
+
continue
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const child = new OverrideSet({
|
|
38
|
+
parent: this,
|
|
39
|
+
key,
|
|
40
|
+
overrides: childOverrides,
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
this.children.set(child.key, child)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
getEdgeRule (edge) {
|
|
48
|
+
for (const rule of this.ruleset.values()) {
|
|
49
|
+
if (rule.name !== edge.name) {
|
|
50
|
+
continue
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (rule.keySpec === '' ||
|
|
54
|
+
semver.intersects(edge.spec, rule.keySpec)) {
|
|
55
|
+
return rule
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return this
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
getNodeRule (node) {
|
|
63
|
+
for (const rule of this.ruleset.values()) {
|
|
64
|
+
if (rule.name !== node.name) {
|
|
65
|
+
continue
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (rule.keySpec === '' ||
|
|
69
|
+
semver.satisfies(node.version, rule.keySpec) ||
|
|
70
|
+
semver.satisfies(node.version, rule.value)) {
|
|
71
|
+
return rule
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return this
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
getMatchingRule (node) {
|
|
79
|
+
for (const rule of this.ruleset.values()) {
|
|
80
|
+
if (rule.name !== node.name) {
|
|
81
|
+
continue
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (rule.keySpec === '' ||
|
|
85
|
+
semver.satisfies(node.version, rule.keySpec) ||
|
|
86
|
+
semver.satisfies(node.version, rule.value)) {
|
|
87
|
+
return rule
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return null
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
* ancestry () {
|
|
95
|
+
for (let ancestor = this; ancestor; ancestor = ancestor.parent) {
|
|
96
|
+
yield ancestor
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
get isRoot () {
|
|
101
|
+
return !this.parent
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
get ruleset () {
|
|
105
|
+
const ruleset = new Map()
|
|
106
|
+
|
|
107
|
+
for (const override of this.ancestry()) {
|
|
108
|
+
for (const kid of override.children.values()) {
|
|
109
|
+
if (!ruleset.has(kid.key)) {
|
|
110
|
+
ruleset.set(kid.key, kid)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (!override.isRoot && !ruleset.has(override.key)) {
|
|
115
|
+
ruleset.set(override.key, override)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return ruleset
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
module.exports = OverrideSet
|
package/lib/place-dep.js
CHANGED
|
@@ -295,6 +295,7 @@ class PlaceDep {
|
|
|
295
295
|
integrity: dep.integrity,
|
|
296
296
|
legacyPeerDeps: this.legacyPeerDeps,
|
|
297
297
|
error: dep.errors[0],
|
|
298
|
+
...(dep.overrides ? { overrides: dep.overrides } : {}),
|
|
298
299
|
...(dep.isLink ? { target: dep.target, realpath: dep.realpath } : {}),
|
|
299
300
|
})
|
|
300
301
|
|
package/lib/printable.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// helper function to output a clearer visualization
|
|
2
2
|
// of the current node and its descendents
|
|
3
|
-
|
|
4
3
|
const localeCompare = require('@isaacs/string-locale-compare')('en')
|
|
5
4
|
const util = require('util')
|
|
6
5
|
const relpath = require('./relpath.js')
|
|
@@ -65,6 +64,11 @@ class ArboristNode {
|
|
|
65
64
|
this.errors = tree.errors.map(treeError)
|
|
66
65
|
}
|
|
67
66
|
|
|
67
|
+
if (tree.overrides) {
|
|
68
|
+
this.overrides = new Map([...tree.overrides.ruleset.values()]
|
|
69
|
+
.map((override) => [override.key, override.value]))
|
|
70
|
+
}
|
|
71
|
+
|
|
68
72
|
// edgesOut sorted by name
|
|
69
73
|
if (tree.edgesOut.size) {
|
|
70
74
|
this.edgesOut = new Map([...tree.edgesOut.entries()]
|
|
@@ -126,7 +130,10 @@ class Edge {
|
|
|
126
130
|
constructor (edge) {
|
|
127
131
|
this.type = edge.type
|
|
128
132
|
this.name = edge.name
|
|
129
|
-
this.spec = edge.
|
|
133
|
+
this.spec = edge.rawSpec || '*'
|
|
134
|
+
if (edge.rawSpec !== edge.spec) {
|
|
135
|
+
this.override = edge.spec
|
|
136
|
+
}
|
|
130
137
|
if (edge.error) {
|
|
131
138
|
this.error = edge.error
|
|
132
139
|
}
|
|
@@ -145,6 +152,8 @@ class EdgeOut extends Edge {
|
|
|
145
152
|
|
|
146
153
|
[util.inspect.custom] () {
|
|
147
154
|
return `{ ${this.type} ${this.name}@${this.spec}${
|
|
155
|
+
this.override ? ` overridden:${this.override}` : ''
|
|
156
|
+
}${
|
|
148
157
|
this.to ? ' -> ' + this.to : ''
|
|
149
158
|
}${
|
|
150
159
|
this.error ? ' ' + this.error : ''
|
package/lib/shrinkwrap.js
CHANGED
|
@@ -1085,18 +1085,29 @@ class Shrinkwrap {
|
|
|
1085
1085
|
return lock
|
|
1086
1086
|
}
|
|
1087
1087
|
|
|
1088
|
-
|
|
1088
|
+
toJSON () {
|
|
1089
1089
|
if (!this.data) {
|
|
1090
|
-
throw new Error('run load() before
|
|
1090
|
+
throw new Error('run load() before getting or setting data')
|
|
1091
1091
|
}
|
|
1092
1092
|
|
|
1093
|
+
return this.commit()
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
toString (options = {}) {
|
|
1097
|
+
const data = this.toJSON()
|
|
1093
1098
|
const { format = true } = options
|
|
1094
1099
|
const defaultIndent = this.indent || 2
|
|
1095
1100
|
const indent = format === true ? defaultIndent
|
|
1096
1101
|
: format || 0
|
|
1097
1102
|
const eol = format ? this.newline || '\n' : ''
|
|
1098
|
-
|
|
1099
|
-
|
|
1103
|
+
return stringify(data, swKeyOrder, indent).replace(/\n/g, eol)
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
save (options = {}) {
|
|
1107
|
+
if (!this.data) {
|
|
1108
|
+
throw new Error('run load() before saving data')
|
|
1109
|
+
}
|
|
1110
|
+
const json = this.toString(options)
|
|
1100
1111
|
return Promise.all([
|
|
1101
1112
|
writeFile(this.filename, json).catch(er => {
|
|
1102
1113
|
if (this.hiddenLockfile) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@npmcli/arborist",
|
|
3
|
-
"version": "4.0
|
|
3
|
+
"version": "4.2.0",
|
|
4
4
|
"description": "Manage node_modules trees",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@isaacs/string-locale-compare": "^1.1.0",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"npm-pick-manifest": "^6.1.0",
|
|
25
25
|
"npm-registry-fetch": "^11.0.0",
|
|
26
26
|
"pacote": "^12.0.2",
|
|
27
|
-
"parse-conflict-json": "^
|
|
27
|
+
"parse-conflict-json": "^2.0.1",
|
|
28
28
|
"proc-log": "^1.0.0",
|
|
29
29
|
"promise-all-reject-late": "^1.0.0",
|
|
30
30
|
"promise-call-limit": "^1.0.1",
|
|
@@ -37,10 +37,11 @@
|
|
|
37
37
|
"walk-up-path": "^1.0.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@npmcli/template-oss": "^2.3.
|
|
40
|
+
"@npmcli/template-oss": "^2.3.1",
|
|
41
41
|
"benchmark": "^2.1.4",
|
|
42
42
|
"chalk": "^4.1.0",
|
|
43
43
|
"minify-registry-metadata": "^2.1.0",
|
|
44
|
+
"nock": "^13.2.0",
|
|
44
45
|
"tap": "^15.1.2",
|
|
45
46
|
"tcompare": "^5.0.6"
|
|
46
47
|
},
|
|
@@ -93,7 +94,7 @@
|
|
|
93
94
|
"engines": {
|
|
94
95
|
"node": "^12.13.0 || ^14.15.0 || >=16"
|
|
95
96
|
},
|
|
96
|
-
"templateVersion": "2.3.
|
|
97
|
+
"templateVersion": "2.3.1",
|
|
97
98
|
"eslintIgnore": [
|
|
98
99
|
"test/fixtures/",
|
|
99
100
|
"!test/fixtures/*.js"
|