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