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 b488be12 Ryan Dahl
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 d30e76e0 isaacs
deps that don't meet a peer requirement are marked with peerInvalid:true
54 b488be12 Ryan Dahl

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 911b0fdd isaacs
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 b488be12 Ryan Dahl
103 911b0fdd isaacs
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 b488be12 Ryan Dahl
109
module.exports = readInstalled
110
111 911b0fdd isaacs
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 b488be12 Ryan Dahl
    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 33a9ac60 isaacs
    obj = copy(data)
138
139 b488be12 Ryan Dahl
    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 13198357 isaacs
    //if (depth >= maxDepth) return cb(null, obj)
198 b488be12 Ryan Dahl
    asyncMap(installed, function (pkg, cb) {
199
      var rv = obj.dependencies[pkg]
200
      if (!rv && obj.devDependencies) rv = obj.devDependencies[pkg]
201 13198357 isaacs
      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 b488be12 Ryan Dahl
      readInstalled_( path.resolve(folder, "node_modules/"+pkg)
214
                    , obj, pkg, obj.dependencies[pkg], depth + 1, maxDepth
215
                    , cb )
216 13198357 isaacs
217 b488be12 Ryan Dahl
    }, function (er, installedData) {
218
      if (er) return cb(er)
219
      installedData.forEach(function (dep) {
220
        obj.dependencies[dep.realName] = dep
221
      })
222 25410096 isaacs
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 b488be12 Ryan Dahl
      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 5aef65a9 isaacs
  findUnmet(obj)
253 b488be12 Ryan Dahl
}
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 5aef65a9 isaacs
264 b488be12 Ryan Dahl
  //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 33a9ac60 isaacs
            // url deps presumed innocent.
282
            && !url.parse(deps[d]).protocol
283 b488be12 Ryan Dahl
            && !semver.satisfies(found.version, deps[d])) {
284
          // the bad thing will happen
285 13198357 isaacs
          log.warn("unmet dependency", obj.path + " requires "+d+"@'"+deps[d]
286 b488be12 Ryan Dahl
                  +"' but will load\n"
287
                  +found.path+",\nwhich is version "+found.version
288 13198357 isaacs
                  )
289 b488be12 Ryan Dahl
          found.invalid = true
290 2bcb9ab7 isaacs
        } else {
291
          found.extraneous = false
292 b488be12 Ryan Dahl
        }
293
        deps[d] = found
294
      }
295 25410096 isaacs
296 b488be12 Ryan Dahl
    })
297 d30e76e0 isaacs
298
  var peerDeps = obj.peerDependencies = obj.peerDependencies || {}
299
  Object.keys(peerDeps).forEach(function (d) {
300 5aef65a9 isaacs
    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 d30e76e0 isaacs
    dependency.extraneous = false
316
317
    if (!semver.satisfies(dependency.version, peerDeps[d])) {
318
      dependency.peerInvalid = true
319
    }
320
  })
321
322 13198357 isaacs
  log.verbose("readInstalled", "returning", obj._id)
323 b488be12 Ryan Dahl
  return obj
324
}
325
326 33a9ac60 isaacs
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
}