@ -13,22 +13,30 @@ var npm = require("./npm.js")
, log = require ( "npmlog" )
, log = require ( "npmlog" )
, path = require ( "path" )
, path = require ( "path" )
, archy = require ( "archy" )
, archy = require ( "archy" )
, semver = require ( "semver" )
ls . usage = "npm ls"
ls . usage = "npm ls"
ls . completion = require ( "./utils/completion/installed-deep.js" )
function ls ( args , silent , cb ) {
function ls ( args , silent , cb ) {
if ( typeof cb !== "function" ) cb = silent , silent = false
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 , ".." )
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 ) {
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 )
if ( er || silent ) return cb ( er , data , lite )
var long = npm . config . get ( "long" )
var long = npm . config . get ( "long" )
@ -36,7 +44,7 @@ function ls (args, silent, cb) {
, out
, out
if ( json ) {
if ( json ) {
var seen = [ ]
var seen = [ ]
var d = long ? bfsify ( data ) : lite
var d = long ? bfs : lite
// the raw data can be circular
// the raw data can be circular
out = JSON . stringify ( d , function ( k , o ) {
out = JSON . stringify ( d , function ( k , o ) {
if ( typeof o === "object" ) {
if ( typeof o === "object" ) {
@ -46,14 +54,19 @@ function ls (args, silent, cb) {
return o
return o
} , 2 )
} , 2 )
} else if ( npm . config . get ( "parseable" ) ) {
} else if ( npm . config . get ( "parseable" ) ) {
out = makeParseable ( bfsify ( data ) , long , dir )
out = makeParseable ( bfs , long , dir )
} else if ( data ) {
} else if ( data ) {
out = makeArchy ( bfsify ( data ) , long , dir )
out = makeArchy ( bfs , long , dir )
}
}
output . write ( out , function ( er ) { cb ( er , data , lite ) } )
output . write ( out , function ( er ) { cb ( er , data , lite ) } )
} )
} )
}
}
// only include
function filter ( data , args ) {
}
function alphasort ( a , b ) {
function alphasort ( a , b ) {
a = a . toLowerCase ( )
a = a . toLowerCase ( )
b = b . toLowerCase ( )
b = b . toLowerCase ( )
@ -124,7 +137,7 @@ function getLite (data, noname) {
return lite
return lite
}
}
function bfsify ( root , current , queue , seen ) {
function bfsify ( root , args , current , queue , seen ) {
// walk over the data, and turn it from this:
// walk over the data, and turn it from this:
// +-- a
// +-- a
// | `-- b
// | `-- b
@ -134,6 +147,7 @@ function bfsify (root, current, queue, seen) {
// +-- a
// +-- a
// `-- b
// `-- b
// which looks nicer
// which looks nicer
args = args || [ ]
current = current || root
current = current || root
queue = queue || [ ]
queue = queue || [ ]
seen = seen || [ root ]
seen = seen || [ root ]
@ -153,10 +167,37 @@ function bfsify (root, current, queue, seen) {
queue . push ( dep )
queue . push ( dep )
seen . 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 ) {
function makeArchy ( data , long , dir ) {
var out = makeArchy_ ( data , long , dir , 0 )
var out = makeArchy_ ( data , long , dir , 0 )
@ -178,8 +219,11 @@ function makeArchy_ (data, long, dir, depth, parent, d) {
var out = { }
var out = { }
// the top level is a bit special.
// the top level is a bit special.
out . label = data . _ id ? data . _ id + " " : ""
out . label = data . _ id || ""
if ( data . link ) out . label += "-> " + data . link
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 . invalid ) {
if ( data . realName !== data . name ) out . label += " (" + data . realName + ")"
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 ) {
. sort ( alphasort ) . map ( function ( d ) {
return makeParseable ( data . dependencies [ d ] , long , dir , depth + 1 , data , d )
return makeParseable ( data . dependencies [ d ] , long , dir , depth + 1 , data , d )
} ) )
} ) )
. filter ( function ( x ) { return x } )
. join ( "\n" )
. join ( "\n" )
}
}
function makeParseable_ ( data , long , dir , depth , parent , d ) {
function makeParseable_ ( data , long , dir , depth , parent , d ) {
if ( data . hasOwnProperty ( "_found" ) && data . _ found !== true ) return ""
if ( typeof data === "string" ) {
if ( typeof data === "string" ) {
if ( data . depth < npm . config . get ( "depth" ) ) {
if ( data . depth < npm . config . get ( "depth" ) ) {
var p = parent . link || parent . path
var p = parent . link || parent . path