mirror of
https://github.com/zhigang1992/npm.git
synced 2026-06-15 10:18:01 +08:00
229 lines
7.0 KiB
JavaScript
229 lines
7.0 KiB
JavaScript
|
|
// show the installed versions of a package
|
|
|
|
module.exports = exports = ls
|
|
|
|
var npm = require("../npm")
|
|
, readInstalled = require("./utils/read-installed")
|
|
, registry = require("./utils/registry")
|
|
, semver = require("./utils/semver")
|
|
, output = require("./utils/output")
|
|
, log = require("./utils/log")
|
|
|
|
ls.usage = "npm ls [some search terms ...]"
|
|
|
|
ls.completion = function (args, index, cb) {
|
|
var compl = []
|
|
, getCompletions = require("./utils/completion/get-completions")
|
|
, name = (args.length + 1 === index) && args[args.length - 1] || ""
|
|
, priors = name ? args.slice(0, args.length - 1) : args
|
|
log.warn(priors, "addToCompl")
|
|
// get the batch of data that matches so far.
|
|
// this is an example of using npm.commands.ls programmatically
|
|
// to fetch data that has been filtered by a set of arguments.
|
|
ls(priors, true, 3600, function (er, data) {
|
|
if (er) return cb(er)
|
|
Object.keys(data).forEach(function (name) {
|
|
compl.push.apply(compl, [name].concat(data[name].words))
|
|
})
|
|
var last = null
|
|
compl = compl.sort(strcmp).filter(function (c) {
|
|
if (args.indexOf(c) !== -1) return false
|
|
var r = c !== last
|
|
last = c
|
|
return r
|
|
})
|
|
var matches = getCompletions(name || "", compl)
|
|
if (name && !name.match(/^=/)) {
|
|
matches = matches.concat(getCompletions("="+name, compl))
|
|
}
|
|
cb(null, matches)
|
|
})
|
|
}
|
|
|
|
function ls (args, silent, staleness, cb_) {
|
|
if (typeof cb_ !== "function") cb_ = staleness, staleness = 600
|
|
if (typeof cb_ !== "function") cb_ = silent, silent = false
|
|
var listopts = npm.config.get("listopts") || ""
|
|
listopts = listopts.split(/\s+/)
|
|
getFilteredData(staleness, listopts.concat(args), function (er, data) {
|
|
// now data is the list of data that we want to show.
|
|
// prettify and print it, and then provide the raw
|
|
// data to the cb.
|
|
if (er) return cb_(er)
|
|
function cb (er) { return cb_(er, data) }
|
|
if (silent) return cb()
|
|
output.write(npm.config.get("outfd"), prettify(data, args), cb)
|
|
})
|
|
}
|
|
|
|
function getFilteredData (staleness, args, cb) {
|
|
var processedData = {}
|
|
getMergedData(staleness, function (er, data) {
|
|
if (er) return cb(er)
|
|
Object.keys(data).sort(strcmp).forEach(function (p) {
|
|
var pkg = data[p]
|
|
if (pkg.url && (!pkg.versions || !Object.keys(pkg.versions).length)) {
|
|
processedData[pkg.name] = getWords(pkg)
|
|
} else {
|
|
Object.keys(pkg.versions).sort(semver.compare).forEach(function (v) {
|
|
pkg.name = p
|
|
processedData[pkg.name+"@"+v] = getWords(pkg, v)
|
|
})
|
|
}
|
|
})
|
|
var filtered = {}
|
|
Object.keys(processedData).forEach(function (name) {
|
|
var d = processedData[name]
|
|
, pass = true
|
|
, test = [name].concat(d.words)
|
|
.concat(d.description)
|
|
.concat(d.keywords)
|
|
for (var i = 0, l = args.length; i < l; i ++) {
|
|
pass = false
|
|
for (var ii = 0, ll = test.length; ii < ll; ii ++) {
|
|
if (test[ii].indexOf(args[i]) !== -1) {
|
|
pass = true
|
|
break
|
|
}
|
|
}
|
|
if (!pass) break
|
|
}
|
|
if (pass) filtered[name] = d
|
|
})
|
|
return cb(null, filtered)
|
|
})
|
|
}
|
|
function getWords (pkg, version) {
|
|
var d = { data : pkg
|
|
, words : []
|
|
, name : pkg.name + (version ? "@" + version : "")
|
|
}
|
|
if (pkg.maintainers && pkg.maintainers.length) {
|
|
d.words.push.apply(d.words, pkg.maintainers.map(function (m) {
|
|
return "=" + m.name
|
|
}))
|
|
}
|
|
var desc = pkg.description
|
|
, kw = pkg.keywords
|
|
if (pkg.url && !version) {
|
|
d.words.push("<"+pkg.url+">")
|
|
} else {
|
|
var v = pkg.versions[version]
|
|
if (v.description) desc = v.description
|
|
if (v.keywords) kw = v.keywords
|
|
if (v.installed) d.words.push("installed")
|
|
if (v.remote) d.words.push("remote")
|
|
if (v.tags.length) d.words.push.apply(d.words, v.tags)
|
|
}
|
|
if (!kw) kw = []
|
|
if (!Array.isArray(kw)) kw = kw.split(/\s+/)
|
|
d.keywords = kw
|
|
d.description = desc || ""
|
|
return d
|
|
}
|
|
function getMergedData (staleness, cb) {
|
|
getData(staleness, function (er, remote, installed) {
|
|
if (er) return cb(er)
|
|
return cb(null, merge(installed, remote))
|
|
})
|
|
}
|
|
function getData (staleness, cb_) {
|
|
var installed
|
|
, remote
|
|
, errState
|
|
readInstalled([], function (er, inst) {
|
|
if (er) return cb_(errState = er)
|
|
installed = inst
|
|
next()
|
|
})
|
|
registry.get("/", null, staleness, function (er, rem) {
|
|
if (errState) return
|
|
if (er) rem = {}
|
|
remote = rem
|
|
next()
|
|
})
|
|
function next () {
|
|
if (!remote || !installed) return
|
|
cb_(null, remote, installed)
|
|
}
|
|
}
|
|
function strcmp (a, b) {
|
|
a = a.toLowerCase()
|
|
b = b.toLowerCase()
|
|
return a === b ? 0 : a > b ? 1 : -1
|
|
}
|
|
function prettify (data, args) {
|
|
var pkgs = Object.keys(data)
|
|
, attrs = []
|
|
, names = []
|
|
, pretty = []
|
|
, beginAttrList = 28
|
|
pkgs.forEach(function (name) {
|
|
var pkg = data[name]
|
|
pretty.push({name:name,attrs:data[name].words.sort(strcmp).join(" ")
|
|
,description:data[name].description
|
|
,keywords:data[name].keywords})
|
|
})
|
|
var colors = [36, 32, 33, 31, 35 ]
|
|
, c = 0
|
|
, l = colors.length
|
|
var maxNameLen = 0
|
|
var space = " "
|
|
pretty.forEach(function (line) {
|
|
maxNameLen = Math.min(Math.max(maxNameLen, line.name.length), space.length)
|
|
})
|
|
maxNameLen += 2
|
|
|
|
// turn each line obj into a single line, only as much ws as necessary.
|
|
try { var cols = process.binding("stdio").getColumns() }
|
|
catch (ex) { cols = Infinity }
|
|
pretty = pretty.map(function (line) {
|
|
var addSpace = maxNameLen - line.name.length
|
|
return (line.name + (space.substr(0, addSpace) || "") + " "
|
|
+ (line.description ? line.description + " " : "")
|
|
+ line.attrs
|
|
+ (line.keywords.length ? " " + line.keywords.join(" ") : ""))
|
|
.substr(0, cols)
|
|
})
|
|
|
|
if (args && pretty.length) args.forEach(function (arg) {
|
|
pretty = pretty.map(function (line) {
|
|
return line.split(arg).join("\033["+colors[c]+"m" + arg + "\033[m")
|
|
})
|
|
c = (c + 1) % l
|
|
})
|
|
if (!pretty.length) pretty = ["Nothing found"]
|
|
pretty.push("")
|
|
return pretty.join("\n")
|
|
}
|
|
function merge (installed, remote) {
|
|
var merged = {}
|
|
// first, just copy the installed stuff
|
|
for (var p in installed) {
|
|
merged[p] = {versions:{}}
|
|
for (var v in installed[p]) {
|
|
merged[p].versions[v] = { installed : true, tags : [] }
|
|
for (var s in installed[p][v]) {
|
|
merged[p].versions[v][s] = installed[p][v][s]
|
|
}
|
|
}
|
|
}
|
|
// now merge in the remote stuff.
|
|
for (var p in remote) {
|
|
merged[p] = merged[p] || {versions:{}}
|
|
for (var d in remote[p]) if (!merged[p].hasOwnProperty(d)) {
|
|
merged[p][d] = remote[p][d]
|
|
}
|
|
for (var v in remote[p].versions) {
|
|
merged[p].versions[v] = merged[p].versions[v] || {}
|
|
merged[p].versions[v].remote = true
|
|
merged[p].versions[v].tags = []
|
|
Object.keys(remote[p]["dist-tags"]).forEach(function (tag) {
|
|
if (remote[p]["dist-tags"][tag] === v) merged[p].versions[v].tags.push(tag)
|
|
})
|
|
}
|
|
}
|
|
return merged
|
|
}
|