1 |
|
2 |
|
3 | module.exports = view
|
4 | view.usage = "npm view pkg[@version] [<field>[.subfield]...]"
|
5 |
|
6 | view.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 |
|
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 |
|
51 | var 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 |
|
61 | function 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 |
|
95 | function fetchAndRead (nv, args, silent, cb) {
|
96 |
|
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 |
|
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 |
|
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 |
|
153 | function cleanBlanks (obj) {
|
154 | var clean = {}
|
155 | Object.keys(obj).forEach(function (version) {
|
156 | clean[version] = obj[version][""]
|
157 | })
|
158 | return clean
|
159 | }
|
160 |
|
161 | function 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 |
|
172 | function 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 |
|
182 | function 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 |
|
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 |
|
234 | function 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 | }
|
263 | function 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 | }
|
294 | function unparsePerson (d) {
|
295 | if (typeof d === "string") return d
|
296 | return d.name
|
297 | + (d.email ? " <"+d.email+">" : "")
|
298 | + (d.url ? " ("+d.url+")" : "")
|
299 | }
|