Browse files

Positional args for ls

  • Loading branch information...
1 parent c2b9940 commit 3f3a0f91d71f46b0f62cf4879a990b90e2ca5f84 @isaacs isaacs committed Jul 10, 2012
Showing with 75 additions and 22 deletions.
  1. +13 −7 doc/cli/list.md
  2. +62 −15 lib/ls.js
View
20 doc/cli/list.md
@@ -3,20 +3,26 @@ npm-ls(1) -- List installed packages
## SYNOPSIS
- npm list
- npm ls
- npm la
- npm ll
+ npm list [<pkg> ...]
+ npm ls [<pkg> ...]
+ npm la [<pkg> ...]
+ npm ll [<pkg> ...]
## DESCRIPTION
This command will print to stdout all the versions of packages that are
installed, as well as their dependencies, in a tree-structure.
-It does not take positional arguments, though you may set config flags
-like with any other command, such as `-g` to list global packages.
+Positional arguments are `name@version-range` identifiers, which will
+limit the results to only the paths to the packages named. Note that
+nested packages will *also* show the paths to the specified packages.
+For example, running `npm ls promzard` in npm's source tree will show:
-It will print out extraneous, missing, and invalid packages.
+ npm@@VERSION@ /path/to/npm
+ └─┬ init-package-json@0.0.4
+ └── promzard@0.1.5
+
+It will show print out extraneous, missing, and invalid packages.
When run as `ll` or `la`, it shows extended information by default.
View
77 lib/ls.js
@@ -13,30 +13,38 @@ var npm = require("./npm.js")
, log = require("npmlog")
, path = require("path")
, archy = require("archy")
+ , semver = require("semver")
ls.usage = "npm ls"
+ls.completion = require("./utils/completion/installed-deep.js")
+
function ls (args, silent, cb) {
if (typeof cb !== "function") cb = silent, silent = false
- if (args.length) {
- // TODO: it would actually be nice to maybe show the locally
- // installed packages only matching the argument names.
- log.warn("ls doesn't take positional args. Try the 'search' command")
- }
-
var dir = path.resolve(npm.dir, "..")
+ // npm ls 'foo@~1.3' bar 'baz@<2'
+ if (!args) args = []
+ else args = args.map(function (a) {
+ var nv = a.split("@")
+ , name = nv.shift()
+ , ver = semver.validRange(nv.join("@")) || ""
+
+ return [ name, ver ]
+ })
+
readInstalled(dir, npm.config.get("depth"), function (er, data) {
- var lite = getLite(bfsify(data))
+ var bfs = bfsify(data, args)
+ , lite = getLite(bfs)
if (er || silent) return cb(er, data, lite)
var long = npm.config.get("long")
, json = npm.config.get("json")
, out
if (json) {
var seen = []
- var d = long ? bfsify(data) : lite
+ var d = long ? bfs : lite
// the raw data can be circular
out = JSON.stringify(d, function (k, o) {
if (typeof o === "object") {
@@ -46,14 +54,19 @@ function ls (args, silent, cb) {
return o
}, 2)
} else if (npm.config.get("parseable")) {
- out = makeParseable(bfsify(data), long, dir)
+ out = makeParseable(bfs, long, dir)
} else if (data) {
- out = makeArchy(bfsify(data), long, dir)
+ out = makeArchy(bfs, long, dir)
}
output.write(out, function (er) { cb(er, data, lite) })
})
}
+// only include
+function filter (data, args) {
+
+}
+
function alphasort (a, b) {
a = a.toLowerCase()
b = b.toLowerCase()
@@ -124,7 +137,7 @@ function getLite (data, noname) {
return lite
}
-function bfsify (root, current, queue, seen) {
+function bfsify (root, args, current, queue, seen) {
// walk over the data, and turn it from this:
// +-- a
// | `-- b
@@ -134,6 +147,7 @@ function bfsify (root, current, queue, seen) {
// +-- a
// `-- b
// which looks nicer
+ args = args || []
current = current || root
queue = queue || []
seen = seen || [root]
@@ -153,10 +167,37 @@ function bfsify (root, current, queue, seen) {
queue.push(dep)
seen.push(dep)
})
- if (!queue.length) return root
- return bfsify(root, queue.shift(), queue, seen)
+
+ if (!queue.length) {
+ // if there were args, then only show the paths to found nodes.
+ return filterFound(root, args)
+ }
+ return bfsify(root, args, queue.shift(), queue, seen)
}
+function filterFound (root, args) {
+ if (!args.length) return root
+ var deps = root.dependencies
+ if (deps) Object.keys(deps).forEach(function (d) {
+ var dep = filterFound(deps[d], args)
+
+ // see if this one itself matches
+ var found = false
+ for (var i = 0; !found && i < args.length; i ++) {
+ if (d === args[i][0]) {
+ found = semver.satisfies(dep.version, args[i][1])
+ }
+ }
+ // included explicitly
+ if (found) dep._found = true
+ // included because a child was included
+ if (dep._found && !root._found) root._found = 1
+ // not included
+ if (!dep._found) delete deps[d]
+ })
+ if (!root._found) root._found = false
+ return root
+}
function makeArchy (data, long, dir) {
var out = makeArchy_(data, long, dir, 0)
@@ -178,8 +219,11 @@ function makeArchy_ (data, long, dir, depth, parent, d) {
var out = {}
// the top level is a bit special.
- out.label = data._id ? data._id + " " : ""
- if (data.link) out.label += "-> " + data.link
+ out.label = data._id || ""
+ if (data._found === true && data._id) {
+ out.label = "\033[33;40m" + out.label.trim() + "\033[m "
+ }
+ if (data.link) out.label += " -> " + data.link
if (data.invalid) {
if (data.realName !== data.name) out.label += " ("+data.realName+")"
@@ -237,10 +281,13 @@ function makeParseable (data, long, dir, depth, parent, d) {
.sort(alphasort).map(function (d) {
return makeParseable(data.dependencies[d], long, dir, depth + 1, data, d)
}))
+ .filter(function (x) { return x })
.join("\n")
}
function makeParseable_ (data, long, dir, depth, parent, d) {
+ if (data.hasOwnProperty("_found") && data._found !== true) return ""
+
if (typeof data === "string") {
if (data.depth < npm.config.get("depth")) {
var p = parent.link || parent.path

0 comments on commit 3f3a0f9

Please sign in to comment.