@npmcli/arborist 2.2.4 → 2.2.8

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.
@@ -431,11 +431,13 @@ module.exports = cls => class IdealTreeBuilder extends cls {
431
431
  // ie, doing `foo@bar` we just return foo
432
432
  // but if it's a url or git, we don't know the name until we
433
433
  // fetch it and look in its manifest.
434
- return Promise.all(add.map(rawSpec =>
435
- this[_retrieveSpecName](npa(rawSpec))
434
+ return Promise.all(add.map(rawSpec => {
435
+ // We do NOT provide the path here, because user-additions need
436
+ // to be resolved relative to the CWD the user is in.
437
+ return this[_retrieveSpecName](npa(rawSpec))
436
438
  .then(add => this[_updateFilePath](add))
437
439
  .then(add => this[_followSymlinkPath](add))
438
- )).then(add => {
440
+ })).then(add => {
439
441
  this[_resolvedAdd] = add
440
442
  // now add is a list of spec objects with names.
441
443
  // find a home for each of them!
@@ -881,6 +883,8 @@ This is a one-time fix-up, please be patient...
881
883
  // create a virtual root node with the same deps as the node that
882
884
  // is requesting this one, so that we can get all the peer deps in
883
885
  // a context where they're likely to be resolvable.
886
+ // Note that the virtual root will also have virtual copies of the
887
+ // targets of any child Links, so that they resolve appropriately.
884
888
  const parent = parent_ || this[_virtualRoot](edge.from)
885
889
  const realParent = edge.peer ? edge.from.resolveParent : edge.from
886
890
 
@@ -934,11 +938,23 @@ This is a one-time fix-up, please be patient...
934
938
  return this[_virtualRoots].get(node)
935
939
 
936
940
  const vr = new Node({
937
- path: '/virtual-root',
941
+ path: node.realpath,
938
942
  sourceReference: node,
939
943
  legacyPeerDeps: this.legacyPeerDeps,
940
944
  })
941
945
 
946
+ // also need to set up any targets from any link deps, so that
947
+ // they are properly reflected in the virtual environment
948
+ for (const child of node.children.values()) {
949
+ if (child.isLink) {
950
+ new Node({
951
+ path: child.realpath,
952
+ sourceReference: child.target,
953
+ root: vr,
954
+ })
955
+ }
956
+ }
957
+
942
958
  this[_virtualRoots].set(node, vr)
943
959
  return vr
944
960
  }
@@ -28,6 +28,7 @@
28
28
 
29
29
  const {resolve} = require('path')
30
30
  const {homedir} = require('os')
31
+ const procLog = require('../proc-log.js')
31
32
 
32
33
  const mixins = [
33
34
  require('../tracker.js'),
@@ -54,6 +55,7 @@ class Arborist extends Base {
54
55
  path: options.path || '.',
55
56
  cache: options.cache || `${homedir()}/.npm/_cacache`,
56
57
  packumentCache: new Map(),
58
+ log: options.log || procLog,
57
59
  }
58
60
  this.cache = resolve(this.options.cache)
59
61
  this.path = resolve(this.options.path)
@@ -6,6 +6,7 @@ const rpj = require('read-package-json-fast')
6
6
  const { updateDepSpec } = require('../dep-spec.js')
7
7
  const AuditReport = require('../audit-report.js')
8
8
  const {subset} = require('semver')
9
+ const npa = require('npm-package-arg')
9
10
 
10
11
  const {dirname, resolve, relative} = require('path')
11
12
  const {depth: dfwalk} = require('treeverse')
@@ -881,11 +882,17 @@ module.exports = cls => class Reifier extends cls {
881
882
 
882
883
  process.emit('time', 'reify:save')
883
884
 
885
+ // resolvedAdd is the list of user add requests, but with names added
886
+ // to things like git repos and tarball file/urls. However, if the
887
+ // user requested 'foo@', and we have a foo@file:../foo, then we should
888
+ // end up saving the spec we actually used, not whatever they gave us.
884
889
  if (this[_resolvedAdd]) {
885
890
  const root = this.idealTree
886
891
  const pkg = root.package
887
- for (const req of this[_resolvedAdd]) {
888
- const {name, rawSpec, subSpec} = req
892
+ for (const { name } of this[_resolvedAdd]) {
893
+ const req = npa(root.edgesOut.get(name).spec, root.realpath)
894
+ const {rawSpec, subSpec} = req
895
+
889
896
  const spec = subSpec ? subSpec.rawSpec : rawSpec
890
897
  const child = root.children.get(name)
891
898
 
@@ -910,6 +917,15 @@ module.exports = cls => class Reifier extends cls {
910
917
  const save = h.https && h.auth ? `git+${h.https(opt)}`
911
918
  : h.shortcut(opt)
912
919
  updateDepSpec(pkg, name, save)
920
+ } else if (req.type === 'directory' || req.type === 'file') {
921
+ // save the relative path in package.json
922
+ // Normally saveSpec is updated with the proper relative
923
+ // path already, but it's possible to specify a full absolute
924
+ // path initially, in which case we can end up with the wrong
925
+ // thing, so just get the ultimate fetchSpec and relativize it.
926
+ const p = req.fetchSpec.replace(/^file:/, '')
927
+ const rel = relpath(root.realpath, p)
928
+ updateDepSpec(pkg, name, `file:${rel}`)
913
929
  } else
914
930
  updateDepSpec(pkg, name, req.saveSpec)
915
931
  }
package/lib/edge.js CHANGED
@@ -87,16 +87,24 @@ class Edge {
87
87
 
88
88
  // return the edge data, and an explanation of how that edge came to be here
89
89
  [_explain] (seen) {
90
- const { error, from } = this
90
+ const { error, from, bundled } = this
91
91
  return {
92
92
  type: this.type,
93
93
  name: this.name,
94
94
  spec: this.spec,
95
+ ...(bundled ? { bundled } : {}),
95
96
  ...(error ? { error } : {}),
96
97
  ...(from ? { from: from.explain(null, seen) } : {}),
97
98
  }
98
99
  }
99
100
 
101
+ get bundled () {
102
+ if (!this.from)
103
+ return false
104
+ const { package: { bundleDependencies = [] } } = this.from
105
+ return bundleDependencies.includes(this.name)
106
+ }
107
+
100
108
  get workspace () {
101
109
  return this[_type] === 'workspace'
102
110
  }
package/lib/printable.js CHANGED
@@ -63,6 +63,13 @@ class ArboristNode {
63
63
  }
64
64
  }
65
65
 
66
+ class ArboristVirtualNode extends ArboristNode {
67
+ constructor (tree, path) {
68
+ super(tree, path)
69
+ this.sourceReference = printableTree(tree.sourceReference, path)
70
+ }
71
+ }
72
+
66
73
  class ArboristLink extends ArboristNode {
67
74
  constructor (tree, path) {
68
75
  super(tree, path)
@@ -119,10 +126,14 @@ class EdgeIn extends Edge {
119
126
  }
120
127
 
121
128
  const printableTree = (tree, path = []) => {
122
- if (path.includes(tree))
123
- return { location: tree.location }
129
+ const Cls = tree.isLink ? ArboristLink
130
+ : tree.sourceReference ? ArboristVirtualNode
131
+ : ArboristNode
132
+ if (path.includes(tree)) {
133
+ const obj = Object.create(Cls.prototype)
134
+ return Object.assign(obj, { location: tree.location })
135
+ }
124
136
  path.push(tree)
125
- const Cls = tree.isLink ? ArboristLink : ArboristNode
126
137
  return new Cls(tree, path)
127
138
  }
128
139
 
package/lib/shrinkwrap.js CHANGED
@@ -32,6 +32,7 @@ const mismatch = (a, b) => a && b && a !== b
32
32
  // After calling this.commit(), any nodes not present in the tree will have
33
33
  // been removed from the shrinkwrap data as well.
34
34
 
35
+ const procLog = require('./proc-log.js')
35
36
  const YarnLock = require('./yarn-lock.js')
36
37
  const {promisify} = require('util')
37
38
  const rimraf = promisify(require('rimraf'))
@@ -39,7 +40,24 @@ const fs = require('fs')
39
40
  const readFile = promisify(fs.readFile)
40
41
  const writeFile = promisify(fs.writeFile)
41
42
  const stat = promisify(fs.stat)
42
- const readdir = promisify(fs.readdir)
43
+ const readdir_ = promisify(fs.readdir)
44
+ const readlink = promisify(fs.readlink)
45
+
46
+ // XXX remove when drop support for node v10
47
+ const lstat = promisify(fs.lstat)
48
+ /* istanbul ignore next - version specific polyfill */
49
+ const readdir = async (path, opt) => {
50
+ if (!opt || !opt.withFileTypes)
51
+ return readdir_(path, opt)
52
+ const ents = await readdir_(path, opt)
53
+ if (typeof ents[0] === 'string') {
54
+ return Promise.all(ents.map(async ent => {
55
+ return Object.assign(await lstat(path + '/' + ent), { name: ent })
56
+ }))
57
+ }
58
+ return ents
59
+ }
60
+
43
61
  const { resolve, basename } = require('path')
44
62
  const specFromLock = require('./spec-from-lock.js')
45
63
  const versionFromTgz = require('./version-from-tgz.js')
@@ -159,10 +177,19 @@ const assertNoNewer = async (path, data, lockTime, dir = path, seen = null) => {
159
177
  : readdir(parent, { withFileTypes: true })
160
178
 
161
179
  return children.catch(() => [])
162
- .then(ents => Promise.all(
163
- ents.filter(ent => ent.isDirectory() && !/^\./.test(ent.name))
164
- .map(ent => assertNoNewer(path, data, lockTime, resolve(parent, ent.name), seen))
165
- )).then(() => {
180
+ .then(ents => Promise.all(ents.map(async ent => {
181
+ const child = resolve(parent, ent.name)
182
+ if (ent.isDirectory() && !/^\./.test(ent.name))
183
+ await assertNoNewer(path, data, lockTime, child, seen)
184
+ else if (ent.isSymbolicLink()) {
185
+ const target = resolve(parent, await readlink(child))
186
+ const tstat = await stat(target).catch(() => null)
187
+ seen.add(relpath(path, child))
188
+ if (tstat && tstat.isDirectory() && !seen.has(relpath(path, target)))
189
+ await assertNoNewer(path, data, lockTime, target, seen)
190
+ }
191
+ })))
192
+ .then(() => {
166
193
  if (dir !== path)
167
194
  return
168
195
 
@@ -265,7 +292,10 @@ class Shrinkwrap {
265
292
  newline = '\n',
266
293
  shrinkwrapOnly = false,
267
294
  hiddenLockfile = false,
295
+ log = procLog,
268
296
  } = options
297
+
298
+ this.log = log
269
299
  this[_awaitingUpdate] = new Map()
270
300
  this.tree = null
271
301
  this.path = resolve(path || '.')
@@ -398,6 +428,8 @@ class Shrinkwrap {
398
428
  // all good! hidden lockfile is the newest thing in here.
399
429
  return data
400
430
  }).catch(er => {
431
+ const rel = relpath(this.path, this.filename)
432
+ this.log.verbose('shrinkwrap', `failed to load ${rel}`, er)
401
433
  this.loadingError = er
402
434
  this.loadedFromDisk = false
403
435
  this.ancientLockfile = false
package/lib/tracker.js CHANGED
@@ -1,13 +1,12 @@
1
- const procLog = require('./proc-log.js')
2
-
3
1
  const _progress = Symbol('_progress')
4
2
  const _onError = Symbol('_onError')
3
+ const procLog = require('./proc-log.js')
5
4
 
6
5
  module.exports = cls => class Tracker extends cls {
7
6
  constructor (options = {}) {
8
7
  super(options)
9
- this[_progress] = new Map()
10
8
  this.log = options.log || procLog
9
+ this[_progress] = new Map()
11
10
  }
12
11
 
13
12
  addTracker (section, subsection = null, key = null) {
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@npmcli/arborist",
3
- "version": "2.2.4",
3
+ "version": "2.2.8",
4
4
  "description": "Manage node_modules trees",
5
5
  "dependencies": {
6
- "@npmcli/installed-package-contents": "^1.0.6",
6
+ "@npmcli/installed-package-contents": "^1.0.7",
7
7
  "@npmcli/map-workspaces": "^1.0.2",
8
8
  "@npmcli/metavuln-calculator": "^1.1.0",
9
9
  "@npmcli/move-file": "^1.1.0",
@@ -46,7 +46,8 @@
46
46
  "tcompare": "^3.0.4"
47
47
  },
48
48
  "scripts": {
49
- "test": "tap",
49
+ "test": "npm run test-only --",
50
+ "test-only": "tap",
50
51
  "posttest": "npm run lint",
51
52
  "snap": "tap",
52
53
  "postsnap": "npm run lint",
@@ -76,12 +77,16 @@
76
77
  },
77
78
  "tap": {
78
79
  "100": true,
79
- "node-arg": [
80
- "--unhandled-rejections=strict"
81
- ],
82
80
  "after": "test/fixtures/cleanup.js",
83
81
  "coverage-map": "map.js",
84
82
  "esm": false,
83
+ "test-env": [
84
+ "NODE_OPTIONS=--no-warnings"
85
+ ],
86
+ "node-arg": [
87
+ "--no-warnings",
88
+ "--no-deprecation"
89
+ ],
85
90
  "timeout": "120"
86
91
  }
87
92
  }