mirror of https://github.com/lukechilds/node.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
169 lines
5.2 KiB
169 lines
5.2 KiB
|
|
// show the installed versions of packages
|
|
//
|
|
// --parseable creates output like this:
|
|
// <fullpath>:<name@ver>:<realpath>:<flags>
|
|
// 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 = path.resolve(npm.dir, "..")
|
|
|
|
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
|
|
}
|
|
|