UNPKG

8.24 kBJavaScriptView Raw
1// npm view [pkg [pkg ...]]
2
3module.exports = view
4view.usage = "npm view pkg[@version] [<field>[.subfield]...]"
5
6view.completion = function (opts, cb) {
7 if (opts.conf.argv.remain.length <= 2) {
8 return mapToRegistry("-/short", npm.config, function (er, uri) {
9 if (er) return cb(er)
10
11 registry.get(uri, null, cb)
12 })
13 }
14 // have the package, get the fields.
15 var tag = npm.config.get("tag")
16 mapToRegistry(opts.conf.argv.remain[2], npm.config, function (er, uri) {
17 if (er) return cb(er)
18
19 registry.get(uri, null, function (er, d) {
20 if (er) return cb(er)
21 var dv = d.versions[d["dist-tags"][tag]]
22 , fields = []
23 d.versions = Object.keys(d.versions).sort(semver.compareLoose)
24 fields = getFields(d).concat(getFields(dv))
25 cb(null, fields)
26 })
27 })
28
29 function getFields (d, f, pref) {
30 f = f || []
31 if (!d) return f
32 pref = pref || []
33 Object.keys(d).forEach(function (k) {
34 if (k.charAt(0) === "_" || k.indexOf(".") !== -1) return
35 var p = pref.concat(k).join(".")
36 f.push(p)
37 if (Array.isArray(d[k])) {
38 d[k].forEach(function (val, i) {
39 var pi = p + "[" + i + "]"
40 if (val && typeof val === "object") getFields(val, f, [p])
41 else f.push(pi)
42 })
43 return
44 }
45 if (typeof d[k] === "object") getFields(d[k], f, [p])
46 })
47 return f
48 }
49}
50
51var npm = require("./npm.js")
52 , readJson = require("read-package-json")
53 , registry = npm.registry
54 , log = require("npmlog")
55 , util = require("util")
56 , semver = require("semver")
57 , mapToRegistry = require("./utils/map-to-registry.js")
58 , npa = require("npm-package-arg")
59 , path = require("path")
60
61function view (args, silent, cb) {
62 if (typeof cb !== "function") cb = silent, silent = false
63
64 if (!args.length) args = ["."]
65
66 var pkg = args.shift()
67 , nv = npa(pkg)
68 , name = nv.name
69 , local = (name === "." || !name)
70
71 if (npm.config.get("global") && local) {
72 return cb(new Error("Cannot use view command in global mode."))
73 }
74
75 if (local) {
76 var dir = npm.prefix
77 readJson(path.resolve(dir, "package.json"), function (er, d) {
78 d = d || {}
79 if (er && er.code !== "ENOENT" && er.code !== "ENOTDIR") return cb(er)
80 if (!d.name) return cb(new Error("Invalid package.json"))
81
82 var p = d.name
83 nv = npa(p)
84 if (pkg && ~pkg.indexOf("@")) {
85 nv.rawSpec = pkg.split("@")[pkg.indexOf("@")]
86 }
87
88 fetchAndRead(nv, args, silent, cb)
89 })
90 } else {
91 fetchAndRead(nv, args, silent, cb)
92 }
93}
94
95function fetchAndRead (nv, args, silent, cb) {
96 // get the data about this package
97 var name = nv.name
98 , version = nv.rawSpec || npm.config.get("tag")
99
100 mapToRegistry(name, npm.config, function (er, uri) {
101 if (er) return cb(er)
102
103 registry.get(uri, null, function (er, data) {
104 if (er) return cb(er)
105 if (data["dist-tags"] && data["dist-tags"].hasOwnProperty(version)) {
106 version = data["dist-tags"][version]
107 }
108
109 if (data.time && data.time.unpublished) {
110 var u = data.time.unpublished
111 er = new Error("Unpublished by " + u.name + " on " + u.time)
112 er.statusCode = 404
113 er.code = "E404"
114 er.pkgid = data._id
115 return cb(er, data)
116 }
117
118
119 var results = []
120 , error = null
121 , versions = data.versions || {}
122 data.versions = Object.keys(versions).sort(semver.compareLoose)
123 if (!args.length) args = [""]
124
125 // remove readme unless we asked for it
126 if (-1 === args.indexOf("readme")) {
127 delete data.readme
128 }
129
130 Object.keys(versions).forEach(function (v) {
131 if (semver.satisfies(v, version, true)) args.forEach(function (args) {
132 // remove readme unless we asked for it
133 if (-1 === args.indexOf("readme")) {
134 delete versions[v].readme
135 }
136 results.push(showFields(data, versions[v], args))
137 })
138 })
139 results = results.reduce(reducer, {})
140 var retval = results
141
142 if (args.length === 1 && args[0] === "") {
143 retval = cleanBlanks(retval)
144 log.silly("cleanup", retval)
145 }
146
147 if (error || silent) cb(error, retval)
148 else printData(results, data._id, cb.bind(null, error, retval))
149 })
150 })
151}
152
153function cleanBlanks (obj) {
154 var clean = {}
155 Object.keys(obj).forEach(function (version) {
156 clean[version] = obj[version][""]
157 })
158 return clean
159}
160
161function reducer (l, r) {
162 if (r) Object.keys(r).forEach(function (v) {
163 l[v] = l[v] || {}
164 Object.keys(r[v]).forEach(function (t) {
165 l[v][t] = r[v][t]
166 })
167 })
168 return l
169}
170
171// return whatever was printed
172function showFields (data, version, fields) {
173 var o = {}
174 ;[data, version].forEach(function (s) {
175 Object.keys(s).forEach(function (k) {
176 o[k] = s[k]
177 })
178 })
179 return search(o, fields.split("."), version.version, fields)
180}
181
182function search (data, fields, version, title) {
183 var field
184 , tail = fields
185 while (!field && fields.length) field = tail.shift()
186 fields = [field].concat(tail)
187 var o
188 if (!field && !tail.length) {
189 o = {}
190 o[version] = {}
191 o[version][title] = data
192 return o
193 }
194 var index = field.match(/(.+)\[([^\]]+)\]$/)
195 if (index) {
196 field = index[1]
197 index = index[2]
198 if (data.field && data.field.hasOwnProperty(index)) {
199 return search(data[field][index], tail, version, title)
200 } else {
201 field = field + "[" + index + "]"
202 }
203 }
204 if (Array.isArray(data)) {
205 if (data.length === 1) {
206 return search(data[0], fields, version, title)
207 }
208 var results = []
209 data.forEach(function (data, i) {
210 var tl = title.length
211 , newt = title.substr(0, tl-(fields.join(".").length) - 1)
212 + "["+i+"]" + [""].concat(fields).join(".")
213 results.push(search(data, fields.slice(), version, newt))
214 })
215 results = results.reduce(reducer, {})
216 return results
217 }
218 if (!data.hasOwnProperty(field)) return undefined
219 data = data[field]
220 if (tail.length) {
221 if (typeof data === "object") {
222 // there are more fields to deal with.
223 return search(data, tail, version, title)
224 } else {
225 return new Error("Not an object: "+data)
226 }
227 }
228 o = {}
229 o[version] = {}
230 o[version][title] = data
231 return o
232}
233
234function printData (data, name, cb) {
235 var versions = Object.keys(data)
236 , msg = ""
237 , includeVersions = versions.length > 1
238 , includeFields
239
240 versions.forEach(function (v) {
241 var fields = Object.keys(data[v])
242 includeFields = includeFields || (fields.length > 1)
243 fields.forEach(function (f) {
244 var d = cleanup(data[v][f])
245 if (includeVersions || includeFields || typeof d !== "string") {
246 d = cleanup(data[v][f])
247 d = npm.config.get("json")
248 ? JSON.stringify(d, null, 2)
249 : util.inspect(d, false, 5, npm.color)
250 } else if (typeof d === "string" && npm.config.get("json")) {
251 d = JSON.stringify(d)
252 }
253 if (f && includeFields) f += " = "
254 if (d.indexOf("\n") !== -1) d = " \n" + d
255 msg += (includeVersions ? name + "@" + v + " " : "")
256 + (includeFields ? f : "") + d + "\n"
257 })
258 })
259
260 console.log(msg)
261 cb(null, data)
262}
263function cleanup (data) {
264 if (Array.isArray(data)) {
265 if (data.length === 1) {
266 data = data[0]
267 } else {
268 return data.map(cleanup)
269 }
270 }
271 if (!data || typeof data !== "object") return data
272
273 if (typeof data.versions === "object"
274 && data.versions
275 && !Array.isArray(data.versions)) {
276 data.versions = Object.keys(data.versions || {})
277 }
278
279 var keys = Object.keys(data)
280 keys.forEach(function (d) {
281 if (d.charAt(0) === "_") delete data[d]
282 else if (typeof data[d] === "object") data[d] = cleanup(data[d])
283 })
284 keys = Object.keys(data)
285 if (keys.length <= 3
286 && data.name
287 && (keys.length === 1
288 || keys.length === 3 && data.email && data.url
289 || keys.length === 2 && (data.email || data.url))) {
290 data = unparsePerson(data)
291 }
292 return data
293}
294function unparsePerson (d) {
295 if (typeof d === "string") return d
296 return d.name
297 + (d.email ? " <"+d.email+">" : "")
298 + (d.url ? " ("+d.url+")" : "")
299}