1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 | module.exports = exports = ls
|
9 |
|
10 | var npm = require("./npm.js")
|
11 | , readInstalled = require("read-installed")
|
12 | , log = require("npmlog")
|
13 | , path = require("path")
|
14 | , archy = require("archy")
|
15 | , semver = require("semver")
|
16 | , url = require("url")
|
17 | , isGitUrl = require("./utils/is-git-url.js")
|
18 |
|
19 | ls.usage = "npm ls"
|
20 |
|
21 | ls.completion = require("./utils/completion/installed-deep.js")
|
22 |
|
23 | function ls (args, silent, cb) {
|
24 | if (typeof cb !== "function") cb = silent, silent = false
|
25 |
|
26 | var dir = path.resolve(npm.dir, "..")
|
27 |
|
28 |
|
29 | if (!args) args = []
|
30 | else args = args.map(function (a) {
|
31 | var nv = a.split("@")
|
32 | , name = nv.shift()
|
33 | , ver = semver.validRange(nv.join("@")) || ""
|
34 |
|
35 | return [ name, ver ]
|
36 | })
|
37 |
|
38 | var depth = npm.config.get("depth")
|
39 | var opt = { depth: depth, log: log.warn }
|
40 | readInstalled(dir, opt, function (er, data) {
|
41 | var bfs = bfsify(data, args)
|
42 | , lite = getLite(bfs)
|
43 |
|
44 | if (er || silent) return cb(er, data, lite)
|
45 |
|
46 | var long = npm.config.get("long")
|
47 | , json = npm.config.get("json")
|
48 | , out
|
49 | if (json) {
|
50 | var seen = []
|
51 | var d = long ? bfs : lite
|
52 |
|
53 | out = JSON.stringify(d, function (k, o) {
|
54 | if (typeof o === "object") {
|
55 | if (-1 !== seen.indexOf(o)) return "[Circular]"
|
56 | seen.push(o)
|
57 | }
|
58 | return o
|
59 | }, 2)
|
60 | } else if (npm.config.get("parseable")) {
|
61 | out = makeParseable(bfs, long, dir)
|
62 | } else if (data) {
|
63 | out = makeArchy(bfs, long, dir)
|
64 | }
|
65 | console.log(out)
|
66 |
|
67 | if (args.length && !data._found) process.exitCode = 1
|
68 |
|
69 |
|
70 | if (lite.problems && lite.problems.length) {
|
71 | er = lite.problems.join('\n')
|
72 | }
|
73 | cb(er, data, lite)
|
74 | })
|
75 | }
|
76 |
|
77 |
|
78 | function filter (data, args) {
|
79 |
|
80 | }
|
81 |
|
82 | function alphasort (a, b) {
|
83 | a = a.toLowerCase()
|
84 | b = b.toLowerCase()
|
85 | return a > b ? 1
|
86 | : a < b ? -1 : 0
|
87 | }
|
88 |
|
89 | function getLite (data, noname) {
|
90 | var lite = {}
|
91 | , maxDepth = npm.config.get("depth")
|
92 |
|
93 | if (!noname && data.name) lite.name = data.name
|
94 | if (data.version) lite.version = data.version
|
95 | if (data.extraneous) {
|
96 | lite.extraneous = true
|
97 | lite.problems = lite.problems || []
|
98 | lite.problems.push( "extraneous: "
|
99 | + data.name + "@" + data.version
|
100 | + " " + (data.path || "") )
|
101 | }
|
102 |
|
103 | if (data._from)
|
104 | lite.from = data._from
|
105 |
|
106 | if (data._resolved)
|
107 | lite.resolved = data._resolved
|
108 |
|
109 | if (data.invalid) {
|
110 | lite.invalid = true
|
111 | lite.problems = lite.problems || []
|
112 | lite.problems.push( "invalid: "
|
113 | + data.name + "@" + data.version
|
114 | + " " + (data.path || "") )
|
115 | }
|
116 |
|
117 | if (data.peerInvalid) {
|
118 | lite.peerInvalid = true
|
119 | lite.problems = lite.problems || []
|
120 | lite.problems.push( "peer invalid: "
|
121 | + data.name + "@" + data.version
|
122 | + " " + (data.path || "") )
|
123 | }
|
124 |
|
125 | if (data.dependencies) {
|
126 | var deps = Object.keys(data.dependencies)
|
127 | if (deps.length) lite.dependencies = deps.map(function (d) {
|
128 | var dep = data.dependencies[d]
|
129 | if (typeof dep === "string") {
|
130 | lite.problems = lite.problems || []
|
131 | var p
|
132 | if (data.depth >= maxDepth) {
|
133 | p = "max depth reached: "
|
134 | } else {
|
135 | p = "missing: "
|
136 | }
|
137 | p += d + "@" + dep
|
138 | + ", required by "
|
139 | + data.name + "@" + data.version
|
140 | lite.problems.push(p)
|
141 | return [d, { required: dep, missing: true }]
|
142 | }
|
143 | return [d, getLite(dep, true)]
|
144 | }).reduce(function (deps, d) {
|
145 | if (d[1].problems) {
|
146 | lite.problems = lite.problems || []
|
147 | lite.problems.push.apply(lite.problems, d[1].problems)
|
148 | }
|
149 | deps[d[0]] = d[1]
|
150 | return deps
|
151 | }, {})
|
152 | }
|
153 | return lite
|
154 | }
|
155 |
|
156 | function bfsify (root, args, current, queue, seen) {
|
157 |
|
158 |
|
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 |
|
166 | args = args || []
|
167 | current = current || root
|
168 | queue = queue || []
|
169 | seen = seen || [root]
|
170 | var deps = current.dependencies = current.dependencies || {}
|
171 | Object.keys(deps).forEach(function (d) {
|
172 | var dep = deps[d]
|
173 | if (typeof dep !== "object") return
|
174 | if (seen.indexOf(dep) !== -1) {
|
175 | if (npm.config.get("parseable") || !npm.config.get("long")) {
|
176 | delete deps[d]
|
177 | return
|
178 | } else {
|
179 | dep = deps[d] = Object.create(dep)
|
180 | dep.dependencies = {}
|
181 | }
|
182 | }
|
183 | queue.push(dep)
|
184 | seen.push(dep)
|
185 | })
|
186 |
|
187 | if (!queue.length) {
|
188 |
|
189 | return filterFound(root, args)
|
190 | }
|
191 | return bfsify(root, args, queue.shift(), queue, seen)
|
192 | }
|
193 |
|
194 | function filterFound (root, args) {
|
195 | if (!args.length) return root
|
196 | var deps = root.dependencies
|
197 | if (deps) Object.keys(deps).forEach(function (d) {
|
198 | var dep = filterFound(deps[d], args)
|
199 |
|
200 |
|
201 | var found = false
|
202 | for (var i = 0; !found && i < args.length; i ++) {
|
203 | if (d === args[i][0]) {
|
204 | found = semver.satisfies(dep.version, args[i][1], true)
|
205 | }
|
206 | }
|
207 |
|
208 | if (found) dep._found = true
|
209 |
|
210 | if (dep._found && !root._found) root._found = 1
|
211 |
|
212 | if (!dep._found) delete deps[d]
|
213 | })
|
214 | if (!root._found) root._found = false
|
215 | return root
|
216 | }
|
217 |
|
218 | function makeArchy (data, long, dir) {
|
219 | var out = makeArchy_(data, long, dir, 0)
|
220 | return archy(out, "", { unicode: npm.config.get("unicode") })
|
221 | }
|
222 |
|
223 | function makeArchy_ (data, long, dir, depth, parent, d) {
|
224 | var color = npm.color
|
225 | if (typeof data === "string") {
|
226 | if (depth < npm.config.get("depth")) {
|
227 |
|
228 | var p = parent.link || parent.path
|
229 | var unmet = "UNMET DEPENDENCY"
|
230 | if (color) {
|
231 | unmet = "\033[31;40m" + unmet + "\033[0m"
|
232 | }
|
233 | data = unmet + " " + d + " " + data
|
234 | } else {
|
235 | data = d+"@"+ data
|
236 | }
|
237 | return data
|
238 | }
|
239 |
|
240 | var out = {}
|
241 |
|
242 | out.label = data._id || ""
|
243 | if (data._found === true && data._id) {
|
244 | var pre = color ? "\033[33;40m" : ""
|
245 | , post = color ? "\033[m" : ""
|
246 | out.label = pre + out.label.trim() + post + " "
|
247 | }
|
248 | if (data.link) out.label += " -> " + data.link
|
249 |
|
250 | if (data.invalid) {
|
251 | if (data.realName !== data.name) out.label += " ("+data.realName+")"
|
252 | out.label += " " + (color ? "\033[31;40m" : "")
|
253 | + "invalid"
|
254 | + (color ? "\033[0m" : "")
|
255 | }
|
256 |
|
257 | if (data.peerInvalid) {
|
258 | out.label += " " + (color ? "\033[31;40m" : "")
|
259 | + "peer invalid"
|
260 | + (color ? "\033[0m" : "")
|
261 | }
|
262 |
|
263 | if (data.extraneous && data.path !== dir) {
|
264 | out.label += " " + (color ? "\033[32;40m" : "")
|
265 | + "extraneous"
|
266 | + (color ? "\033[0m" : "")
|
267 | }
|
268 |
|
269 |
|
270 | if (data._resolved) {
|
271 | var p = url.parse(data._resolved)
|
272 | if (isGitUrl(p))
|
273 | out.label += " (" + data._resolved + ")"
|
274 | }
|
275 |
|
276 | if (long) {
|
277 | if (dir === data.path) out.label += "\n" + dir
|
278 | out.label += "\n" + getExtras(data, dir)
|
279 | } else if (dir === data.path) {
|
280 | if (out.label) out.label += " "
|
281 | out.label += dir
|
282 | }
|
283 |
|
284 |
|
285 | out.nodes = Object.keys(data.dependencies || {})
|
286 | .sort(alphasort).map(function (d) {
|
287 | return makeArchy_(data.dependencies[d], long, dir, depth + 1, data, d)
|
288 | })
|
289 |
|
290 | if (out.nodes.length === 0 && data.path === dir) {
|
291 | out.nodes = ["(empty)"]
|
292 | }
|
293 |
|
294 | return out
|
295 | }
|
296 |
|
297 | function getExtras (data, dir) {
|
298 | var extras = []
|
299 |
|
300 | if (data.description) extras.push(data.description)
|
301 | if (data.repository) extras.push(data.repository.url)
|
302 | if (data.homepage) extras.push(data.homepage)
|
303 | if (data._from) {
|
304 | var from = data._from
|
305 | if (from.indexOf(data.name + "@") === 0) {
|
306 | from = from.substr(data.name.length + 1)
|
307 | }
|
308 | var u = url.parse(from)
|
309 | if (u.protocol) extras.push(from)
|
310 | }
|
311 | return extras.join("\n")
|
312 | }
|
313 |
|
314 |
|
315 | function makeParseable (data, long, dir, depth, parent, d) {
|
316 | depth = depth || 0
|
317 |
|
318 | return [ makeParseable_(data, long, dir, depth, parent, d) ]
|
319 | .concat(Object.keys(data.dependencies || {})
|
320 | .sort(alphasort).map(function (d) {
|
321 | return makeParseable(data.dependencies[d], long, dir, depth + 1, data, d)
|
322 | }))
|
323 | .filter(function (x) { return x })
|
324 | .join("\n")
|
325 | }
|
326 |
|
327 | function makeParseable_ (data, long, dir, depth, parent, d) {
|
328 | if (data.hasOwnProperty("_found") && data._found !== true) return ""
|
329 |
|
330 | if (typeof data === "string") {
|
331 | if (data.depth < npm.config.get("depth")) {
|
332 | var p = parent.link || parent.path
|
333 | data = npm.config.get("long")
|
334 | ? path.resolve(parent.path, "node_modules", d)
|
335 | + ":"+d+"@"+JSON.stringify(data)+":INVALID:MISSING"
|
336 | : ""
|
337 | } else {
|
338 | data = path.resolve(data.path || "", "node_modules", d || "")
|
339 | + (npm.config.get("long")
|
340 | ? ":" + d + "@" + JSON.stringify(data)
|
341 | + ":"
|
342 | + ":MAXDEPTH"
|
343 | : "")
|
344 | }
|
345 |
|
346 | return data
|
347 | }
|
348 |
|
349 | if (!npm.config.get("long")) return data.path
|
350 |
|
351 | return data.path
|
352 | + ":" + (data._id || "")
|
353 | + ":" + (data.realPath !== data.path ? data.realPath : "")
|
354 | + (data.extraneous ? ":EXTRANEOUS" : "")
|
355 | + (data.invalid ? ":INVALID" : "")
|
356 | + (data.peerInvalid ? ":PEERINVALID" : "")
|
357 | }
|