// show the installed versions of packages // // --parseable creates output like this: // ::: // Flags are a :-separated list of zero or more indicators module.exports = exports = ls var npm = require("../npm.js") , readInstalled = require("./utils/read-installed.js") , output = require("./utils/output.js") , log = require("./utils/log.js") , relativize = require("./utils/relativize.js") , path = require("path") ls.usage = "npm ls" function ls (args, silent, cb) { if (typeof cb !== "function") cb = silent, silent = false if (args.length) { log.warn("ls doesn't take positional args. Try the 'search' command") } var dir = npm.prefix if (npm.config.get("global")) dir = path.resolve(dir, "lib") readInstalled(dir, function (er, data) { if (er || silent) return cb(er, data) var long = npm.config.get("long") var out = makePretty(bfsify(data), long, dir).join("\n") output.write(out, function (er) { cb(er, data) }) }) } function bfsify (root, current, queue, seen) { // walk over the data, and turn it from this: // +-- a // | `-- b // | `-- a (truncated) // `--b (truncated) // into this: // +-- a // `-- b // which looks nicer current = current || root queue = queue || [] seen = seen || [] var deps = current.dependencies = current.dependencies || {} Object.keys(deps).forEach(function (d) { var dep = deps[d] if (typeof dep !== "object") return if (seen.indexOf(dep) !== -1) { if (npm.config.get("parseable") || !npm.config.get("long")) { delete deps[d] return } else { dep = deps[d] = Object.create(dep) dep.dependencies = {} } } queue.push(dep) seen.push(dep) }) if (!queue.length) return root return bfsify(root, queue.shift(), queue, seen) } function makePretty (data, long, dir, prefix, list) { var top = !list list = list || [] prefix = prefix || "" list.push(format(data, long, prefix, dir)) var deps = data.dependencies || {} , childPref = prefix.split("├─").join("│ ") .split("└─").join(" ") , depList = Object.keys(deps) , depLast = depList.length - 1 , maxDepth = npm.config.get("depth") Object.keys(deps).sort(function (a, b) { return a > b ? 1 : -1 }).forEach(function (d, i) { var depData = deps[d] if (typeof depData === "string") { if (data.depth < maxDepth) { var p = data.link || data.path log.warn("Unmet dependency in "+p, d+" "+deps[d]) depData = npm.config.get("parseable") ? ( npm.config.get("long") ? path.resolve(data.path, "node_modules", d) + ":"+d+"@"+JSON.stringify(depData)+":INVALID:MISSING" : "" ) : "─ \033[31;40mUNMET DEPENDENCY\033[0m "+d+" "+depData } else { if (npm.config.get("parseable")) { depData = path.resolve(data.path, "node_modules", d) + (npm.config.get("long") ? ":" + d + "@" + JSON.stringify(depData) + ":" // no realpath resolved + ":MAXDEPTH" : "") } else { depData = "─ "+d+"@'"+depData +"' (max depth reached)" } } } var c = i === depLast ? "└─" : "├─" makePretty(depData, long, dir, childPref + c, list) }) if (top && list.length === 1 && !data._id) { if (!npm.config.get("parseable")) { list.push("(empty)") } else if (npm.config.get("long")) list[0] += ":EMPTY" } return list.filter(function (l) { return l && l.trim() }) } function ugly (data) { if (typeof data === "string") { return data } if (!npm.config.get("long")) return data.path return data.path + ":" + (data._id || "") + ":" + (data.realPath !== data.path ? data.realPath : "") + (data.extraneous ? ":EXTRANEOUS" : "") + (data.invalid ? ":INVALID" : "") } function format (data, long, prefix, dir) { if (npm.config.get("parseable")) return ugly(data) if (typeof data === "string") { return prefix + data } // console.log([data.path, dir], "relativize") var depLen = Object.keys(data.dependencies).length , space = prefix.split("├─").join("│ ") .split("└─").join(" ") + (depLen ? "" : " ") , rel = relativize(data.path || "", dir) , l = prefix + (rel === "." ? "" : depLen ? "┬ " : "─ ") + (data._id ? data._id + " " : "") + (data.link ? "-> " + data.link : "") + "" + (rel === "." && !(long && data._id) ? dir : "") if (data.invalid) { if (data.realName !== data.name) l += " ("+data.realName+")" l += " \033[31;40minvalid\033[0m" } if (data.extraneous && rel !== ".") { l += " \033[32;40mextraneous\033[0m" } if (!long || !data._id) return l var extras = [] if (rel !== ".") extras.push(rel) else extras.push(dir) if (data.description) extras.push(data.description) if (data.repository) extras.push(data.repository.url) if (data.homepage) extras.push(data.homepage) extras = extras.filter(function (e) { return e }) var lastExtra = !depLen && extras.length - 1 l += extras.map(function (e, i) { var indent = !depLen ? " " : "│ " return "\n" + space + indent + e }).join("") return l }