@npmcli/arborist 4.1.0 → 4.2.1
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/lib/arborist/build-ideal-tree.js +9 -4
- package/lib/arborist/reify.js +87 -19
- package/lib/shrinkwrap.js +28 -13
- package/package.json +8 -6
|
@@ -41,7 +41,7 @@ const _complete = Symbol('complete')
|
|
|
41
41
|
const _depsSeen = Symbol('depsSeen')
|
|
42
42
|
const _depsQueue = Symbol('depsQueue')
|
|
43
43
|
const _currentDep = Symbol('currentDep')
|
|
44
|
-
const _updateAll = Symbol('updateAll')
|
|
44
|
+
const _updateAll = Symbol.for('updateAll')
|
|
45
45
|
const _mutateTree = Symbol('mutateTree')
|
|
46
46
|
const _flagsSuspect = Symbol.for('flagsSuspect')
|
|
47
47
|
const _workspaces = Symbol.for('workspaces')
|
|
@@ -176,7 +176,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
176
176
|
// public method
|
|
177
177
|
async buildIdealTree (options = {}) {
|
|
178
178
|
if (this.idealTree) {
|
|
179
|
-
return
|
|
179
|
+
return this.idealTree
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
// allow the user to set reify options on the ctor as well.
|
|
@@ -194,8 +194,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
194
194
|
process.emit('time', 'idealTree')
|
|
195
195
|
|
|
196
196
|
if (!options.add && !options.rm && !options.update && this[_global]) {
|
|
197
|
-
|
|
198
|
-
return Promise.reject(er)
|
|
197
|
+
throw new Error('global requires add, rm, or update option')
|
|
199
198
|
}
|
|
200
199
|
|
|
201
200
|
// first get the virtual tree, if possible. If there's a lockfile, then
|
|
@@ -334,6 +333,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
|
|
|
334
333
|
root.meta.lockfileVersion = defaultLockfileVersion
|
|
335
334
|
}
|
|
336
335
|
}
|
|
336
|
+
root.meta.inferFormattingOptions(root.package)
|
|
337
337
|
return root
|
|
338
338
|
})
|
|
339
339
|
|
|
@@ -1180,6 +1180,11 @@ This is a one-time fix-up, please be patient...
|
|
|
1180
1180
|
return true
|
|
1181
1181
|
}
|
|
1182
1182
|
|
|
1183
|
+
// If the edge is a workspace, and it's valid, leave it alone
|
|
1184
|
+
if (edge.to.isWorkspace) {
|
|
1185
|
+
return false
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1183
1188
|
// user explicitly asked to update this package by name, problem
|
|
1184
1189
|
if (this[_updateNames].includes(edge.name)) {
|
|
1185
1190
|
return true
|
package/lib/arborist/reify.js
CHANGED
|
@@ -58,6 +58,8 @@ const _bundleUnpacked = Symbol('bundleUnpacked')
|
|
|
58
58
|
const _bundleMissing = Symbol('bundleMissing')
|
|
59
59
|
const _reifyNode = Symbol.for('reifyNode')
|
|
60
60
|
const _extractOrLink = Symbol('extractOrLink')
|
|
61
|
+
const _updateAll = Symbol.for('updateAll')
|
|
62
|
+
const _updateNames = Symbol.for('updateNames')
|
|
61
63
|
// defined by rebuild mixin
|
|
62
64
|
const _checkBins = Symbol.for('checkBins')
|
|
63
65
|
const _symlink = Symbol('symlink')
|
|
@@ -1140,21 +1142,33 @@ module.exports = cls => class Reifier extends cls {
|
|
|
1140
1142
|
// for install failures. Those still end up in the shrinkwrap, so we
|
|
1141
1143
|
// save it first, then prune out the optional trash, and then return it.
|
|
1142
1144
|
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
+
const save = !(options.save === false)
|
|
1146
|
+
|
|
1147
|
+
// we check for updates in order to make sure we run save ideal tree
|
|
1148
|
+
// even though save=false since we want `npm update` to be able to
|
|
1149
|
+
// write to package-lock files by default
|
|
1150
|
+
const hasUpdates = this[_updateAll] || this[_updateNames].length
|
|
1151
|
+
|
|
1152
|
+
// we're going to completely skip save ideal tree in case of a global or
|
|
1153
|
+
// dry-run install and also if the save option is set to false, EXCEPT for
|
|
1154
|
+
// update since the expected behavior for npm7+ is for update to
|
|
1155
|
+
// NOT save to package.json, we make that exception since we still want
|
|
1156
|
+
// saveIdealTree to be able to write the lockfile by default.
|
|
1157
|
+
const saveIdealTree = !(
|
|
1158
|
+
(!save && !hasUpdates)
|
|
1159
|
+
|| this[_global]
|
|
1160
|
+
|| this[_dryRun]
|
|
1161
|
+
)
|
|
1162
|
+
|
|
1163
|
+
if (!saveIdealTree) {
|
|
1145
1164
|
return false
|
|
1146
1165
|
}
|
|
1147
1166
|
|
|
1148
1167
|
process.emit('time', 'reify:save')
|
|
1149
1168
|
|
|
1150
1169
|
const updatedTrees = new Set()
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
// to things like git repos and tarball file/urls. However, if the
|
|
1154
|
-
// user requested 'foo@', and we have a foo@file:../foo, then we should
|
|
1155
|
-
// end up saving the spec we actually used, not whatever they gave us.
|
|
1156
|
-
if (this[_resolvedAdd].length) {
|
|
1157
|
-
for (const { name, tree: addTree } of this[_resolvedAdd]) {
|
|
1170
|
+
const updateNodes = nodes => {
|
|
1171
|
+
for (const { name, tree: addTree } of nodes) {
|
|
1158
1172
|
// addTree either the root, or a workspace
|
|
1159
1173
|
const edge = addTree.edgesOut.get(name)
|
|
1160
1174
|
const pkg = addTree.package
|
|
@@ -1168,7 +1182,7 @@ module.exports = cls => class Reifier extends cls {
|
|
|
1168
1182
|
// that we couldn't resolve, this MAY be missing. if we haven't
|
|
1169
1183
|
// blown up by now, it's because it was not a problem, though, so
|
|
1170
1184
|
// just move on.
|
|
1171
|
-
if (!child) {
|
|
1185
|
+
if (!child || !addTree.isTop) {
|
|
1172
1186
|
continue
|
|
1173
1187
|
}
|
|
1174
1188
|
|
|
@@ -1259,6 +1273,63 @@ module.exports = cls => class Reifier extends cls {
|
|
|
1259
1273
|
}
|
|
1260
1274
|
}
|
|
1261
1275
|
|
|
1276
|
+
// helper that retrieves an array of nodes that were
|
|
1277
|
+
// potentially updated during the reify process, in order
|
|
1278
|
+
// to limit the number of nodes to check and update, only
|
|
1279
|
+
// select nodes from the inventory that are direct deps
|
|
1280
|
+
// of a given package.json (project root or a workspace)
|
|
1281
|
+
// and in ase of using a list of `names`, restrict nodes
|
|
1282
|
+
// to only names that are found in this list
|
|
1283
|
+
const retrieveUpdatedNodes = names => {
|
|
1284
|
+
const filterDirectDependencies = node =>
|
|
1285
|
+
!node.isRoot && node.resolveParent.isRoot
|
|
1286
|
+
&& (!names || names.includes(node.name))
|
|
1287
|
+
const directDeps = this.idealTree.inventory
|
|
1288
|
+
.filter(filterDirectDependencies)
|
|
1289
|
+
|
|
1290
|
+
// traverses the list of direct dependencies and collect all nodes
|
|
1291
|
+
// to be updated, since any of them might have changed during reify
|
|
1292
|
+
const nodes = []
|
|
1293
|
+
for (const node of directDeps) {
|
|
1294
|
+
for (const edgeIn of node.edgesIn) {
|
|
1295
|
+
nodes.push({
|
|
1296
|
+
name: node.name,
|
|
1297
|
+
tree: edgeIn.from.target,
|
|
1298
|
+
})
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
return nodes
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
if (save) {
|
|
1305
|
+
// when using update all alongside with save, we'll make
|
|
1306
|
+
// sure to refresh every dependency of the root idealTree
|
|
1307
|
+
if (this[_updateAll]) {
|
|
1308
|
+
const nodes = retrieveUpdatedNodes()
|
|
1309
|
+
updateNodes(nodes)
|
|
1310
|
+
} else {
|
|
1311
|
+
// resolvedAdd is the list of user add requests, but with names added
|
|
1312
|
+
// to things like git repos and tarball file/urls. However, if the
|
|
1313
|
+
// user requested 'foo@', and we have a foo@file:../foo, then we should
|
|
1314
|
+
// end up saving the spec we actually used, not whatever they gave us.
|
|
1315
|
+
if (this[_resolvedAdd].length) {
|
|
1316
|
+
updateNodes(this[_resolvedAdd])
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
// if updating given dependencies by name, restrict the list of
|
|
1320
|
+
// nodes to check to only those currently in _updateNames
|
|
1321
|
+
if (this[_updateNames].length) {
|
|
1322
|
+
const nodes = retrieveUpdatedNodes(this[_updateNames])
|
|
1323
|
+
updateNodes(nodes)
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
// grab any from explicitRequests that had deps removed
|
|
1327
|
+
for (const { from: tree } of this.explicitRequests) {
|
|
1328
|
+
updatedTrees.add(tree)
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1262
1333
|
// preserve indentation, if possible
|
|
1263
1334
|
const {
|
|
1264
1335
|
[Symbol.for('indent')]: indent,
|
|
@@ -1291,15 +1362,12 @@ module.exports = cls => class Reifier extends cls {
|
|
|
1291
1362
|
await pkgJson.save()
|
|
1292
1363
|
}
|
|
1293
1364
|
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
// refresh the edges so they have the correct specs
|
|
1301
|
-
tree.package = tree.package
|
|
1302
|
-
promises.push(updatePackageJson(tree))
|
|
1365
|
+
if (save) {
|
|
1366
|
+
for (const tree of updatedTrees) {
|
|
1367
|
+
// refresh the edges so they have the correct specs
|
|
1368
|
+
tree.package = tree.package
|
|
1369
|
+
promises.push(updatePackageJson(tree))
|
|
1370
|
+
}
|
|
1303
1371
|
}
|
|
1304
1372
|
|
|
1305
1373
|
await Promise.all(promises)
|
package/lib/shrinkwrap.js
CHANGED
|
@@ -424,6 +424,18 @@ class Shrinkwrap {
|
|
|
424
424
|
.map(fn => fn && maybeStatFile(fn)))
|
|
425
425
|
}
|
|
426
426
|
|
|
427
|
+
inferFormattingOptions (packageJSONData) {
|
|
428
|
+
// don't use detect-indent, just pick the first line.
|
|
429
|
+
// if the file starts with {" then we have an indent of '', ie, none
|
|
430
|
+
// which will default to 2 at save time.
|
|
431
|
+
const {
|
|
432
|
+
[Symbol.for('indent')]: indent,
|
|
433
|
+
[Symbol.for('newline')]: newline,
|
|
434
|
+
} = packageJSONData
|
|
435
|
+
this.indent = indent !== undefined ? indent : this.indent
|
|
436
|
+
this.newline = newline !== undefined ? newline : this.newline
|
|
437
|
+
}
|
|
438
|
+
|
|
427
439
|
load () {
|
|
428
440
|
// we don't need to load package-lock.json except for top of tree nodes,
|
|
429
441
|
// only npm-shrinkwrap.json.
|
|
@@ -451,15 +463,7 @@ class Shrinkwrap {
|
|
|
451
463
|
|
|
452
464
|
return data ? parseJSON(data) : {}
|
|
453
465
|
}).then(async data => {
|
|
454
|
-
|
|
455
|
-
// if the file starts with {" then we have an indent of '', ie, none
|
|
456
|
-
// which will default to 2 at save time.
|
|
457
|
-
const {
|
|
458
|
-
[Symbol.for('indent')]: indent,
|
|
459
|
-
[Symbol.for('newline')]: newline,
|
|
460
|
-
} = data
|
|
461
|
-
this.indent = indent !== undefined ? indent : this.indent
|
|
462
|
-
this.newline = newline !== undefined ? newline : this.newline
|
|
466
|
+
this.inferFormattingOptions(data)
|
|
463
467
|
|
|
464
468
|
if (!this.hiddenLockfile || !data.packages) {
|
|
465
469
|
return data
|
|
@@ -1085,18 +1089,29 @@ class Shrinkwrap {
|
|
|
1085
1089
|
return lock
|
|
1086
1090
|
}
|
|
1087
1091
|
|
|
1088
|
-
|
|
1092
|
+
toJSON () {
|
|
1089
1093
|
if (!this.data) {
|
|
1090
|
-
throw new Error('run load() before
|
|
1094
|
+
throw new Error('run load() before getting or setting data')
|
|
1091
1095
|
}
|
|
1092
1096
|
|
|
1097
|
+
return this.commit()
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
toString (options = {}) {
|
|
1101
|
+
const data = this.toJSON()
|
|
1093
1102
|
const { format = true } = options
|
|
1094
1103
|
const defaultIndent = this.indent || 2
|
|
1095
1104
|
const indent = format === true ? defaultIndent
|
|
1096
1105
|
: format || 0
|
|
1097
1106
|
const eol = format ? this.newline || '\n' : ''
|
|
1098
|
-
|
|
1099
|
-
|
|
1107
|
+
return stringify(data, swKeyOrder, indent).replace(/\n/g, eol)
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
save (options = {}) {
|
|
1111
|
+
if (!this.data) {
|
|
1112
|
+
throw new Error('run load() before saving data')
|
|
1113
|
+
}
|
|
1114
|
+
const json = this.toString(options)
|
|
1100
1115
|
return Promise.all([
|
|
1101
1116
|
writeFile(this.filename, json).catch(er => {
|
|
1102
1117
|
if (this.hiddenLockfile) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@npmcli/arborist",
|
|
3
|
-
"version": "4.1
|
|
3
|
+
"version": "4.2.1",
|
|
4
4
|
"description": "Manage node_modules trees",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@isaacs/string-locale-compare": "^1.1.0",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"@npmcli/node-gyp": "^1.0.3",
|
|
13
13
|
"@npmcli/package-json": "^1.0.1",
|
|
14
14
|
"@npmcli/run-script": "^2.0.0",
|
|
15
|
-
"bin-links": "^
|
|
15
|
+
"bin-links": "^3.0.0",
|
|
16
16
|
"cacache": "^15.0.3",
|
|
17
17
|
"common-ancestor-path": "^1.0.1",
|
|
18
18
|
"json-parse-even-better-errors": "^2.3.1",
|
|
@@ -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,7 +37,7 @@
|
|
|
37
37
|
"walk-up-path": "^1.0.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@npmcli/template-oss": "^2.
|
|
40
|
+
"@npmcli/template-oss": "^2.4.2",
|
|
41
41
|
"benchmark": "^2.1.4",
|
|
42
42
|
"chalk": "^4.1.0",
|
|
43
43
|
"minify-registry-metadata": "^2.1.0",
|
|
@@ -94,9 +94,11 @@
|
|
|
94
94
|
"engines": {
|
|
95
95
|
"node": "^12.13.0 || ^14.15.0 || >=16"
|
|
96
96
|
},
|
|
97
|
-
"templateVersion": "2.3.1",
|
|
98
97
|
"eslintIgnore": [
|
|
99
98
|
"test/fixtures/",
|
|
100
99
|
"!test/fixtures/*.js"
|
|
101
|
-
]
|
|
100
|
+
],
|
|
101
|
+
"templateOSS": {
|
|
102
|
+
"version": "2.4.3"
|
|
103
|
+
}
|
|
102
104
|
}
|