|  | 
 | // Walk through the file-system "database" of installed | 
 | // packages, and create a data object related to the | 
 | // installed versions of each package. | 
 |  | 
 | /* | 
 | This will traverse through all node_modules folders, | 
 | resolving the dependencies object to the object corresponding to | 
 | the package that meets that dep, or just the version/range if | 
 | unmet. | 
 |  | 
 | Assuming that you had this folder structure: | 
 |  | 
 | /path/to | 
 | +-- package.json { name = "root" } | 
 | `-- node_modules | 
 |     +-- foo {bar, baz, asdf} | 
 |     | +-- node_modules | 
 |     |   +-- bar { baz } | 
 |     |   `-- baz | 
 |     `-- asdf | 
 |  | 
 | where "foo" depends on bar, baz, and asdf, bar depends on baz, | 
 | and bar and baz are bundled with foo, whereas "asdf" is at | 
 | the higher level (sibling to foo), you'd get this object structure: | 
 |  | 
 | { <package.json data> | 
 | , path: "/path/to" | 
 | , parent: null | 
 | , dependencies: | 
 |   { foo : | 
 |     { version: "1.2.3" | 
 |     , path: "/path/to/node_modules/foo" | 
 |     , parent: <Circular: root> | 
 |     , dependencies: | 
 |       { bar: | 
 |         { parent: <Circular: foo> | 
 |         , path: "/path/to/node_modules/foo/node_modules/bar" | 
 |         , version: "2.3.4" | 
 |         , dependencies: { baz: <Circular: foo.dependencies.baz> } | 
 |         } | 
 |       , baz: { ... } | 
 |       , asdf: <Circular: asdf> | 
 |       } | 
 |     } | 
 |   , asdf: { ... } | 
 |   } | 
 | } | 
 |  | 
 | Unmet deps are left as strings. | 
 | Extraneous deps are marked with extraneous:true | 
 | deps that don't meet a requirement are marked with invalid:true | 
 | deps that don't meet a peer requirement are marked with peerInvalid:true | 
 |  | 
 | to READ(packagefolder, parentobj, name, reqver) | 
 | obj = read package.json | 
 | installed = ./node_modules/* | 
 | if parentobj is null, and no package.json | 
 |   obj = {dependencies:{<installed>:ANY}} | 
 | deps = Object.keys(obj.dependencies) | 
 | obj.path = packagefolder | 
 | obj.parent = parentobj | 
 | if name, && obj.name !== name, obj.invalid = true | 
 | if reqver, && obj.version !satisfies reqver, obj.invalid = true | 
 | if !reqver && parentobj, obj.extraneous = true | 
 | for each folder in installed | 
 |   obj.dependencies[folder] = READ(packagefolder+node_modules+folder, | 
 |                                   obj, folder, obj.dependencies[folder]) | 
 | # walk tree to find unmet deps | 
 | for each dep in obj.dependencies not in installed | 
 |   r = obj.parent | 
 |   while r | 
 |     if r.dependencies[dep] | 
 |       if r.dependencies[dep].verion !satisfies obj.dependencies[dep] | 
 |         WARN | 
 |         r.dependencies[dep].invalid = true | 
 |       obj.dependencies[dep] = r.dependencies[dep] | 
 |       r = null | 
 |     else r = r.parent | 
 | return obj | 
 |  | 
 |  | 
 | TODO: | 
 | 1. Find unmet deps in parent directories, searching as node does up | 
 | as far as the left-most node_modules folder. | 
 | 2. Ignore anything in node_modules that isn't a package folder. | 
 |  | 
 | */ | 
 |  | 
 | try { | 
 |   var fs = require("graceful-fs") | 
 | } catch (er) { | 
 |   var fs = require("fs") | 
 | } | 
 |  | 
 | var path = require("path") | 
 | var asyncMap = require("slide").asyncMap | 
 | var semver = require("semver") | 
 | var readJson = require("read-package-json") | 
 | var url = require("url") | 
 | var util = require("util") | 
 | var extend = require("util-extend") | 
 |  | 
 | var debug = require("debuglog")("read-installed") | 
 |  | 
 | var readdir = require("readdir-scoped-modules") | 
 |  | 
 | // Sentinel catch-all version constraint used when a dependency is not | 
 | // listed in the package.json file. | 
 | var ANY = {} | 
 |  | 
 | module.exports = readInstalled | 
 |  | 
 | function readInstalled (folder, opts, cb) { | 
 |   if (typeof opts === 'function') { | 
 |     cb = opts | 
 |     opts = {} | 
 |   } else { | 
 |     opts = extend({}, opts) | 
 |   } | 
 |  | 
 |   if (typeof opts.depth !== 'number') | 
 |     opts.depth = Infinity | 
 |  | 
 |   opts.depth = Math.max(0, opts.depth) | 
 |  | 
 |   if (typeof opts.log !== 'function') | 
 |     opts.log = function () {} | 
 |  | 
 |   opts.dev = !!opts.dev | 
 |   opts.realpathSeen = {} | 
 |   opts.findUnmetSeen = [] | 
 |  | 
 |  | 
 |   readInstalled_(folder, null, null, null, 0, opts, function (er, obj) { | 
 |     if (er) return cb(er) | 
 |     // now obj has all the installed things, where they're installed | 
 |     // figure out the inheritance links, now that the object is built. | 
 |     resolveInheritance(obj, opts) | 
 |     obj.root = true | 
 |     unmarkExtraneous(obj, opts) | 
 |     cb(null, obj) | 
 |   }) | 
 | } | 
 |  | 
 | function readInstalled_ (folder, parent, name, reqver, depth, opts, cb) { | 
 |   var installed | 
 |     , obj | 
 |     , real | 
 |     , link | 
 |     , realpathSeen = opts.realpathSeen | 
 |  | 
 |   readdir(path.resolve(folder, "node_modules"), function (er, i) { | 
 |     // error indicates that nothing is installed here | 
 |     if (er) i = [] | 
 |     installed = i.filter(function (f) { return f.charAt(0) !== "." }) | 
 |     next() | 
 |   }) | 
 |  | 
 |   readJson(path.resolve(folder, "package.json"), function (er, data) { | 
 |     obj = copy(data) | 
 |  | 
 |     if (!parent) { | 
 |       obj = obj || true | 
 |       er = null | 
 |     } | 
 |     return next(er) | 
 |   }) | 
 |  | 
 |   fs.lstat(folder, function (er, st) { | 
 |     if (er) { | 
 |       if (!parent) real = true | 
 |       return next(er) | 
 |     } | 
 |     fs.realpath(folder, function (er, rp) { | 
 |       debug("realpath(%j) = %j", folder, rp) | 
 |       real = rp | 
 |       if (st.isSymbolicLink()) link = rp | 
 |       next(er) | 
 |     }) | 
 |   }) | 
 |  | 
 |   var errState = null | 
 |     , called = false | 
 |   function next (er) { | 
 |     if (errState) return | 
 |     if (er) { | 
 |       errState = er | 
 |       return cb(null, []) | 
 |     } | 
 |     debug('next', installed, obj && typeof obj, name, real) | 
 |     if (!installed || !obj || !real || called) return | 
 |     called = true | 
 |     if (realpathSeen[real]) return cb(null, realpathSeen[real]) | 
 |     if (obj === true) { | 
 |       obj = {dependencies:{}, path:folder} | 
 |       installed.forEach(function (i) { obj.dependencies[i] = ANY }) | 
 |     } | 
 |     if (name && obj.name !== name) obj.invalid = true | 
 |     obj.realName = name || obj.name | 
 |     obj.dependencies = obj.dependencies || {} | 
 |  | 
 |     // At this point, figure out what dependencies we NEED to get met | 
 |     obj._dependencies = copy(obj.dependencies) | 
 |  | 
 |     if (reqver === ANY) { | 
 |       // We were unable to determine the required version of this | 
 |       // dependency from the package.json file, but we now know its actual | 
 |       // version, so treat that version as the required version to avoid | 
 |       // marking the dependency as invalid below. See #40. | 
 |       reqver = obj.version; | 
 |     } | 
 |  | 
 |     // "foo":"http://blah" and "foo":"latest" are always presumed valid | 
 |     if (reqver | 
 |         && semver.validRange(reqver, true) | 
 |         && !semver.satisfies(obj.version, reqver, true)) { | 
 |       obj.invalid = true | 
 |     } | 
 |  | 
 |     // Mark as extraneous at this point. | 
 |     // This will be un-marked in unmarkExtraneous, where we mark as | 
 |     // not-extraneous everything that is required in some way from | 
 |     // the root object. | 
 |     obj.extraneous = true | 
 |  | 
 |     obj.path = obj.path || folder | 
 |     obj.realPath = real | 
 |     obj.link = link | 
 |     if (parent && !obj.link) obj.parent = parent | 
 |     realpathSeen[real] = obj | 
 |     obj.depth = depth | 
 |     //if (depth >= opts.depth) return cb(null, obj) | 
 |     asyncMap(installed, function (pkg, cb) { | 
 |       var rv = obj.dependencies[pkg] | 
 |       if (!rv && obj.devDependencies && opts.dev) | 
 |         rv = obj.devDependencies[pkg] | 
 |  | 
 |       if (depth > opts.depth) { | 
 |         obj.dependencies = {} | 
 |         return cb(null, obj) | 
 |       } | 
 |  | 
 |       readInstalled_( path.resolve(folder, "node_modules/"+pkg) | 
 |                     , obj, pkg, obj.dependencies[pkg], depth + 1, opts | 
 |                     , cb ) | 
 |  | 
 |     }, function (er, installedData) { | 
 |       if (er) return cb(er) | 
 |       installedData.forEach(function (dep) { | 
 |         obj.dependencies[dep.realName] = dep | 
 |       }) | 
 |  | 
 |       // any strings here are unmet things.  however, if it's | 
 |       // optional, then that's fine, so just delete it. | 
 |       if (obj.optionalDependencies) { | 
 |         Object.keys(obj.optionalDependencies).forEach(function (dep) { | 
 |           if (typeof obj.dependencies[dep] === "string") { | 
 |             delete obj.dependencies[dep] | 
 |           } | 
 |         }) | 
 |       } | 
 |       return cb(null, obj) | 
 |     }) | 
 |   } | 
 | } | 
 |  | 
 | // starting from a root object, call findUnmet on each layer of children | 
 | var riSeen = [] | 
 | function resolveInheritance (obj, opts) { | 
 |   if (typeof obj !== "object") return | 
 |   if (riSeen.indexOf(obj) !== -1) return | 
 |   riSeen.push(obj) | 
 |   if (typeof obj.dependencies !== "object") { | 
 |     obj.dependencies = {} | 
 |   } | 
 |   Object.keys(obj.dependencies).forEach(function (dep) { | 
 |     findUnmet(obj.dependencies[dep], opts) | 
 |   }) | 
 |   Object.keys(obj.dependencies).forEach(function (dep) { | 
 |     if (typeof obj.dependencies[dep] === "object") { | 
 |       resolveInheritance(obj.dependencies[dep], opts) | 
 |     } else { | 
 |       debug("unmet dep! %s %s@%s", obj.name, dep, obj.dependencies[dep]) | 
 |     } | 
 |   }) | 
 |   findUnmet(obj, opts) | 
 | } | 
 |  | 
 | // find unmet deps by walking up the tree object. | 
 | // No I/O | 
 | function findUnmet (obj, opts) { | 
 |   var findUnmetSeen = opts.findUnmetSeen | 
 |   if (findUnmetSeen.indexOf(obj) !== -1) return | 
 |   findUnmetSeen.push(obj) | 
 |   debug("find unmet parent=%s obj=", obj.parent && obj.parent.name, obj.name || obj) | 
 |   var deps = obj.dependencies = obj.dependencies || {} | 
 |  | 
 |   debug(deps) | 
 |   Object.keys(deps) | 
 |     .filter(function (d) { return typeof deps[d] === "string" }) | 
 |     .forEach(function (d) { | 
 |       var found = findDep(obj, d) | 
 |       debug("finding dep %j", d, found && found.name || found) | 
 |       // "foo":"http://blah" and "foo":"latest" are always presumed valid | 
 |       if (typeof deps[d] === "string" && | 
 |           semver.validRange(deps[d], true) && | 
 |           found && | 
 |           !semver.satisfies(found.version, deps[d], true)) { | 
 |         // the bad thing will happen | 
 |         opts.log( "unmet dependency" | 
 |                 , obj.path + " requires "+d+"@'"+deps[d] | 
 |                 + "' but will load\n" | 
 |                 + found.path+",\nwhich is version "+found.version ) | 
 |         found.invalid = true | 
 |       } | 
 |       if (found) { | 
 |         deps[d] = found | 
 |       } | 
 |     }) | 
 |  | 
 |   var peerDeps = obj.peerDependencies = obj.peerDependencies || {} | 
 |   Object.keys(peerDeps).forEach(function (d) { | 
 |     var dependency | 
 |  | 
 |     if (!obj.parent) { | 
 |       dependency = obj.dependencies[d] | 
 |  | 
 |       // read it as a missing dep | 
 |       if (!dependency) { | 
 |         obj.dependencies[d] = peerDeps[d] | 
 |       } | 
 |     } else { | 
 |       var r = obj.parent | 
 |       while (r && !dependency) { | 
 |         dependency = r.dependencies && r.dependencies[d] | 
 |         r = r.link ? null : r.parent | 
 |       } | 
 |     } | 
 |  | 
 |     if (!dependency) { | 
 |       // mark as a missing dep! | 
 |       obj.dependencies[d] = peerDeps[d] | 
 |     } else if (!semver.satisfies(dependency.version, peerDeps[d], true)) { | 
 |       dependency.peerInvalid = true | 
 |     } | 
 |   }) | 
 |  | 
 |   return obj | 
 | } | 
 |  | 
 | function unmarkExtraneous (obj, opts) { | 
 |   // Mark all non-required deps as extraneous. | 
 |   // start from the root object and mark as non-extraneous all modules | 
 |   // that haven't been previously flagged as extraneous then propagate | 
 |   // to all their dependencies | 
 |  | 
 |   obj.extraneous = false | 
 |  | 
 |   var deps = obj._dependencies || [] | 
 |   if (opts.dev && obj.devDependencies && (obj.root || obj.link)) { | 
 |     Object.keys(obj.devDependencies).forEach(function (k) { | 
 |       deps[k] = obj.devDependencies[k] | 
 |     }) | 
 |   } | 
 |  | 
 |   if (obj.peerDependencies) { | 
 |     Object.keys(obj.peerDependencies).forEach(function (k) { | 
 |       deps[k] = obj.peerDependencies[k] | 
 |     }) | 
 |   } | 
 |  | 
 |   debug("not extraneous", obj._id, deps) | 
 |   Object.keys(deps).forEach(function (d) { | 
 |     var dep = findDep(obj, d) | 
 |     if (dep && dep.extraneous) { | 
 |       unmarkExtraneous(dep, opts) | 
 |     } | 
 |   }) | 
 | } | 
 |  | 
 | // Find the one that will actually be loaded by require() | 
 | // so we can make sure it's valid etc. | 
 | function findDep (obj, d) { | 
 |   var r = obj | 
 |     , found = null | 
 |   while (r && !found) { | 
 |     // if r is a valid choice, then use that. | 
 |     // kinda weird if a pkg depends on itself, but after the first | 
 |     // iteration of this loop, it indicates a dep cycle. | 
 |     if (typeof r.dependencies[d] === "object") { | 
 |       found = r.dependencies[d] | 
 |     } | 
 |     if (!found && r.realName === d) found = r | 
 |     r = r.link ? null : r.parent | 
 |   } | 
 |   return found | 
 | } | 
 |  | 
 | function copy (obj) { | 
 |   if (!obj || typeof obj !== 'object') return obj | 
 |   if (Array.isArray(obj)) return obj.map(copy) | 
 |  | 
 |   var o = {} | 
 |   for (var i in obj) o[i] = copy(obj[i]) | 
 |   return o | 
 | } |