The data contained in this repository can be downloaded to your computer using one of several clients.
Please see the documentation of your version control software client for more information.

Please select the desired protocol below to get the URL.

This URL has Read-Only access.

Statistics
| Branch: | Revision:

main_repo / deps / npm / node_modules / read-installed / read-installed.js @ 5aef65a9

History | View | Annotate | Download (9.36 KB)

1

    
2
// Walk through the file-system "database" of installed
3
// packages, and create a data object related to the
4
// installed versions of each package.
5

    
6
/*
7
This will traverse through all node_modules folders,
8
resolving the dependencies object to the object corresponding to
9
the package that meets that dep, or just the version/range if
10
unmet.
11

12
Assuming that you had this folder structure:
13

14
/path/to
15
+-- package.json { name = "root" }
16
`-- node_modules
17
    +-- foo {bar, baz, asdf}
18
    | +-- node_modules
19
    |   +-- bar { baz }
20
    |   `-- baz
21
    `-- asdf
22

23
where "foo" depends on bar, baz, and asdf, bar depends on baz,
24
and bar and baz are bundled with foo, whereas "asdf" is at
25
the higher level (sibling to foo), you'd get this object structure:
26

27
{ <package.json data>
28
, path: "/path/to"
29
, parent: null
30
, dependencies:
31
  { foo :
32
    { version: "1.2.3"
33
    , path: "/path/to/node_modules/foo"
34
    , parent: <Circular: root>
35
    , dependencies:
36
      { bar:
37
        { parent: <Circular: foo>
38
        , path: "/path/to/node_modules/foo/node_modules/bar"
39
        , version: "2.3.4"
40
        , dependencies: { baz: <Circular: foo.dependencies.baz> }
41
        }
42
      , baz: { ... }
43
      , asdf: <Circular: asdf>
44
      }
45
    }
46
  , asdf: { ... }
47
  }
48
}
49

50
Unmet deps are left as strings.
51
Extraneous deps are marked with extraneous:true
52
deps that don't meet a requirement are marked with invalid:true
53
deps that don't meet a peer requirement are marked with peerInvalid:true
54

55
to READ(packagefolder, parentobj, name, reqver)
56
obj = read package.json
57
installed = ./node_modules/*
58
if parentobj is null, and no package.json
59
  obj = {dependencies:{<installed>:"*"}}
60
deps = Object.keys(obj.dependencies)
61
obj.path = packagefolder
62
obj.parent = parentobj
63
if name, && obj.name !== name, obj.invalid = true
64
if reqver, && obj.version !satisfies reqver, obj.invalid = true
65
if !reqver && parentobj, obj.extraneous = true
66
for each folder in installed
67
  obj.dependencies[folder] = READ(packagefolder+node_modules+folder,
68
                                  obj, folder, obj.dependencies[folder])
69
# walk tree to find unmet deps
70
for each dep in obj.dependencies not in installed
71
  r = obj.parent
72
  while r
73
    if r.dependencies[dep]
74
      if r.dependencies[dep].verion !satisfies obj.dependencies[dep]
75
        WARN
76
        r.dependencies[dep].invalid = true
77
      obj.dependencies[dep] = r.dependencies[dep]
78
      r = null
79
    else r = r.parent
80
return obj
81

82

83
TODO:
84
1. Find unmet deps in parent directories, searching as node does up
85
as far as the left-most node_modules folder.
86
2. Ignore anything in node_modules that isn't a package folder.
87

88
*/
89

    
90
try {
91
  var fs = require("graceful-fs")
92
} catch (er) {
93
  var fs = require("fs")
94
}
95

    
96
try {
97
  var log = require("npmlog")
98
} catch (_) {
99
  var log = { verbose: noop, info: noop, warn: noop, error: noop }
100
  function noop () {}
101
}
102

    
103
var path = require("path")
104
var asyncMap = require("slide").asyncMap
105
var semver = require("semver")
106
var readJson = require("read-package-json")
107
var url = require("url")
108

    
109
module.exports = readInstalled
110

    
111
function readInstalled (folder, depth, cb) {
112
  if (typeof cb !== "function") cb = depth, depth = Infinity
113
  readInstalled_(folder, null, null, null, 0, depth, function (er, obj) {
114
    if (er) return cb(er)
115
    // now obj has all the installed things, where they're installed
116
    // figure out the inheritance links, now that the object is built.
117
    resolveInheritance(obj)
118
    cb(null, obj)
119
  })
120
}
121

    
122
var rpSeen = {}
123
function readInstalled_ (folder, parent, name, reqver, depth, maxDepth, cb) {
124
  var installed
125
    , obj
126
    , real
127
    , link
128

    
129
  fs.readdir(path.resolve(folder, "node_modules"), function (er, i) {
130
    // error indicates that nothing is installed here
131
    if (er) i = []
132
    installed = i.filter(function (f) { return f.charAt(0) !== "." })
133
    next()
134
  })
135

    
136
  readJson(path.resolve(folder, "package.json"), function (er, data) {
137
    obj = copy(data)
138

    
139
    if (!parent) {
140
      obj = obj || true
141
      er = null
142
    }
143
    return next(er)
144
  })
145

    
146
  fs.lstat(folder, function (er, st) {
147
    if (er) {
148
      if (!parent) real = true
149
      return next(er)
150
    }
151
    fs.realpath(folder, function (er, rp) {
152
      //console.error("realpath(%j) = %j", folder, rp)
153
      real = rp
154
      if (st.isSymbolicLink()) link = rp
155
      next(er)
156
    })
157
  })
158

    
159
  var errState = null
160
    , called = false
161
  function next (er) {
162
    if (errState) return
163
    if (er) {
164
      errState = er
165
      return cb(null, [])
166
    }
167
    //console.error('next', installed, obj && typeof obj, name, real)
168
    if (!installed || !obj || !real || called) return
169
    called = true
170
    if (rpSeen[real]) return cb(null, rpSeen[real])
171
    if (obj === true) {
172
      obj = {dependencies:{}, path:folder}
173
      installed.forEach(function (i) { obj.dependencies[i] = "*" })
174
    }
175
    if (name && obj.name !== name) obj.invalid = true
176
    obj.realName = name || obj.name
177
    obj.dependencies = obj.dependencies || {}
178

    
179
    // "foo":"http://blah" is always presumed valid
180
    if (reqver
181
        && semver.validRange(reqver)
182
        && !semver.satisfies(obj.version, reqver)) {
183
      obj.invalid = true
184
    }
185

    
186
    if (parent
187
        && !(name in parent.dependencies)
188
        && !(name in (parent.devDependencies || {}))) {
189
      obj.extraneous = true
190
    }
191
    obj.path = obj.path || folder
192
    obj.realPath = real
193
    obj.link = link
194
    if (parent && !obj.link) obj.parent = parent
195
    rpSeen[real] = obj
196
    obj.depth = depth
197
    //if (depth >= maxDepth) return cb(null, obj)
198
    asyncMap(installed, function (pkg, cb) {
199
      var rv = obj.dependencies[pkg]
200
      if (!rv && obj.devDependencies) rv = obj.devDependencies[pkg]
201
      if (depth >= maxDepth) {
202
        // just try to get the version number
203
        var pkgfolder = path.resolve(folder, "node_modules", pkg)
204
          , jsonFile = path.resolve(pkgfolder, "package.json")
205
        return readJson(jsonFile, function (er, depData) {
206
          // already out of our depth, ignore errors
207
          if (er || !depData || !depData.version) return cb(null, obj)
208
          obj.dependencies[pkg] = depData.version
209
          cb(null, obj)
210
        })
211
      }
212

    
213
      readInstalled_( path.resolve(folder, "node_modules/"+pkg)
214
                    , obj, pkg, obj.dependencies[pkg], depth + 1, maxDepth
215
                    , cb )
216

    
217
    }, function (er, installedData) {
218
      if (er) return cb(er)
219
      installedData.forEach(function (dep) {
220
        obj.dependencies[dep.realName] = dep
221
      })
222

    
223
      // any strings here are unmet things.  however, if it's
224
      // optional, then that's fine, so just delete it.
225
      if (obj.optionalDependencies) {
226
        Object.keys(obj.optionalDependencies).forEach(function (dep) {
227
          if (typeof obj.dependencies[dep] === "string") {
228
            delete obj.dependencies[dep]
229
          }
230
        })
231
      }
232
      return cb(null, obj)
233
    })
234
  }
235
}
236

    
237
// starting from a root object, call findUnmet on each layer of children
238
var riSeen = []
239
function resolveInheritance (obj) {
240
  if (typeof obj !== "object") return
241
  if (riSeen.indexOf(obj) !== -1) return
242
  riSeen.push(obj)
243
  if (typeof obj.dependencies !== "object") {
244
    obj.dependencies = {}
245
  }
246
  Object.keys(obj.dependencies).forEach(function (dep) {
247
    findUnmet(obj.dependencies[dep])
248
  })
249
  Object.keys(obj.dependencies).forEach(function (dep) {
250
    resolveInheritance(obj.dependencies[dep])
251
  })
252
  findUnmet(obj)
253
}
254

    
255
// find unmet deps by walking up the tree object.
256
// No I/O
257
var fuSeen = []
258
function findUnmet (obj) {
259
  if (fuSeen.indexOf(obj) !== -1) return
260
  fuSeen.push(obj)
261
  //console.error("find unmet", obj.name, obj.parent && obj.parent.name)
262
  var deps = obj.dependencies = obj.dependencies || {}
263

    
264
  //console.error(deps)
265
  Object.keys(deps)
266
    .filter(function (d) { return typeof deps[d] === "string" })
267
    .forEach(function (d) {
268
      //console.error("find unmet", obj.name, d, deps[d])
269
      var r = obj.parent
270
        , found = null
271
      while (r && !found && typeof deps[d] === "string") {
272
        // if r is a valid choice, then use that.
273
        found = r.dependencies[d]
274
        if (!found && r.realName === d) found = r
275

    
276
        if (!found) {
277
          r = r.link ? null : r.parent
278
          continue
279
        }
280
        if ( typeof deps[d] === "string"
281
            // url deps presumed innocent.
282
            && !url.parse(deps[d]).protocol
283
            && !semver.satisfies(found.version, deps[d])) {
284
          // the bad thing will happen
285
          log.warn("unmet dependency", obj.path + " requires "+d+"@'"+deps[d]
286
                  +"' but will load\n"
287
                  +found.path+",\nwhich is version "+found.version
288
                  )
289
          found.invalid = true
290
        } else {
291
          found.extraneous = false
292
        }
293
        deps[d] = found
294
      }
295

    
296
    })
297

    
298
  var peerDeps = obj.peerDependencies = obj.peerDependencies || {}
299
  Object.keys(peerDeps).forEach(function (d) {
300
    var dependency
301

    
302
    if (!obj.parent) {
303
      dependency = obj.dependencies[d]
304

    
305
      // read it as a missing dep
306
      if (!dependency) {
307
        obj.dependencies[d] = peerDeps[d]
308
      }
309
    } else {
310
      dependency = obj.parent.dependencies && obj.parent.dependencies[d]
311
    }
312

    
313
    if (!dependency) return
314

    
315
    dependency.extraneous = false
316

    
317
    if (!semver.satisfies(dependency.version, peerDeps[d])) {
318
      dependency.peerInvalid = true
319
    }
320
  })
321

    
322
  log.verbose("readInstalled", "returning", obj._id)
323
  return obj
324
}
325

    
326
function copy (obj) {
327
  if (!obj || typeof obj !== 'object') return obj
328
  if (Array.isArray(obj)) return obj.map(copy)
329

    
330
  var o = {}
331
  for (var i in obj) o[i] = copy(obj[i])
332
  return o
333
}