Skip to content

Commit 92dc157

Browse files
committed
feat(view): add workspace support
1 parent 2b3d1f9 commit 92dc157

File tree

2 files changed

+128
-63
lines changed

2 files changed

+128
-63
lines changed

docs/content/commands/npm-view.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ aliases: info, show, v
1414

1515
### Description
1616

17-
This command shows data about a package and prints it to the stream
18-
referenced by the `outfd` config, which defaults to stdout.
17+
This command shows data about a package and prints it to stdout.
1918

2019
As an example, to view information about the `connect` package from the registry, you would run:
2120

lib/view.js

Lines changed: 127 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@ const fs = require('fs')
77
const jsonParse = require('json-parse-even-better-errors')
88
const log = require('npmlog')
99
const npa = require('npm-package-arg')
10-
const path = require('path')
10+
const { resolve } = require('path')
1111
const relativeDate = require('tiny-relative-date')
1212
const semver = require('semver')
1313
const style = require('ansistyles')
1414
const { inspect, promisify } = require('util')
1515
const { packument } = require('pacote')
16+
const getWorkspaces = require('./workspaces/get-workspaces.js')
1617

1718
const readFile = promisify(fs.readFile)
1819
const readJson = async file => jsonParse(await readFile(file, 'utf8'))
@@ -24,6 +25,15 @@ class View extends BaseCommand {
2425
return 'View registry info'
2526
}
2627

28+
/* istanbul ignore next - see test/lib/load-all-commands.js */
29+
static get params () {
30+
return [
31+
'json',
32+
'workspace',
33+
'workspaces',
34+
]
35+
}
36+
2737
/* istanbul ignore next - see test/lib/load-all-commands.js */
2838
static get name () {
2939
return 'view'
@@ -85,43 +95,120 @@ class View extends BaseCommand {
8595
this.view(args).then(() => cb()).catch(cb)
8696
}
8797

98+
execWorkspaces (args, filters, cb) {
99+
this.viewWorkspaces(args, filters).then(() => cb()).catch(cb)
100+
}
101+
88102
async view (args) {
89103
if (!args.length)
90104
args = ['.']
105+
let pkg = args.shift()
106+
const local =/^\.@/.test(pkg) || pkg === '.'
107+
108+
if (local) {
109+
if (this.npm.config.get('global'))
110+
throw new Error('Cannot use view command in global mode.')
111+
const dir = this.npm.prefix
112+
const manifest = await readJson(resolve(dir, 'package.json'))
113+
if (!manifest.name)
114+
throw new Error('Invalid package.json, no "name" field')
115+
// put the version back if it existed
116+
pkg = `${manifest.name}${pkg.slice(1)}`
117+
}
118+
let wholePackument = false
119+
if (!args.length) {
120+
args = ['']
121+
wholePackument = true
122+
}
123+
const [pckmnt, data] = await this.getData(pkg, args)
124+
125+
if (!this.npm.config.get('json') && wholePackument) {
126+
// pretty view (entire packument)
127+
await Promise.all(
128+
data.map((v) => this.prettyView(pckmnt, v[Object.keys(v)[0]]['']))
129+
)
130+
} else {
131+
// JSON formatted output (JSON or specific attributes from packument)
132+
let reducedData = data.reduce(reducer, {})
133+
if (wholePackument) {
134+
// No attributes
135+
reducedData = cleanBlanks(reducedData)
136+
log.silly('view', reducedData)
137+
}
138+
// disable the progress bar entirely, as we can't meaningfully update it if
139+
// we may have partial lines printed.
140+
log.disableProgress()
141+
142+
const msg = await this.jsonData(reducedData, pckmnt._id)
143+
if (msg !== '')
144+
console.log(msg)
145+
}
146+
}
147+
148+
async viewWorkspaces (args, filters) {
149+
if (!args.length) {
150+
args = ['.']
151+
}
152+
153+
let pkg = args.shift()
91154

155+
const local = /^\.@/.test(pkg) || pkg === '.'
156+
if (!local) {
157+
this.npm.log.warn('Ignoring workspaces for remote package')
158+
return this.view([pkg, ...args])
159+
}
160+
161+
let wholePackument = false
162+
if (!args.length) {
163+
wholePackument = true
164+
args = [''] //getData relies on this
165+
}
166+
const results = {}
167+
const workspaces = await getWorkspaces(filters, { path: this.npm.localPrefix })
168+
for (const workspace of [...workspaces.entries()]) {
169+
const wsPkg = `${workspace[0]}${pkg.slice(1)}`
170+
const [pckmnt, data] = await this.getData(wsPkg, args)
171+
172+
let reducedData = data.reduce(reducer, {})
173+
if (wholePackument) {
174+
// No attributes
175+
reducedData = cleanBlanks(reducedData)
176+
log.silly('view', reducedData)
177+
}
178+
179+
if (!this.npm.config.get('json')) {
180+
if (wholePackument) {
181+
data.map((v) => this.prettyView(pckmnt, v[Object.keys(v)[0]]['']))
182+
} else {
183+
console.log(workspace[0])
184+
const msg = await this.jsonData(reducedData, pckmnt._id)
185+
if (msg !== '')
186+
console.log(msg)
187+
}
188+
} else {
189+
const msg = await this.jsonData(reducedData, pckmnt._id)
190+
if (msg !== '')
191+
results[workspace[0]] = JSON.parse(msg)
192+
}
193+
}
194+
if (Object.keys(results).length > 0) {
195+
console.log(results)
196+
}
197+
}
198+
199+
async getData (pkg, args) {
92200
const opts = {
93201
...this.npm.flatOptions,
94202
preferOnline: true,
95203
fullMetadata: true,
96204
}
97-
const pkg = args.shift()
98-
let nv
99-
if (/^[.]@/.test(pkg))
100-
nv = npa.resolve(null, pkg.slice(2))
101-
else
102-
nv = npa(pkg)
103205

104-
const name = nv.name
105-
const local = (name === '.' || !name)
106-
107-
if (this.npm.config.get('global') && local)
108-
throw new Error('Cannot use view command in global mode.')
109-
110-
if (local) {
111-
const dir = this.npm.prefix
112-
const manifest = await readJson(path.resolve(dir, 'package.json'))
113-
if (!manifest.name)
114-
throw new Error('Invalid package.json, no "name" field')
115-
const p = manifest.name
116-
nv = npa(p)
117-
if (pkg && ~pkg.indexOf('@'))
118-
nv.rawSpec = pkg.split('@')[pkg.indexOf('@')]
119-
}
206+
const spec = npa(pkg)
120207

121208
// get the data about this package
122-
let version = nv.rawSpec || this.npm.config.get('tag')
209+
let version = spec.rawSpec || this.npm.config.get('tag')
123210

124-
const pckmnt = await packument(nv, opts)
211+
const pckmnt = await packument(spec, opts)
125212

126213
if (pckmnt['dist-tags'] && pckmnt['dist-tags'][version])
127214
version = pckmnt['dist-tags'][version]
@@ -135,11 +222,9 @@ class View extends BaseCommand {
135222
throw er
136223
}
137224

138-
const results = []
225+
const data = []
139226
const versions = pckmnt.versions || {}
140227
pckmnt.versions = Object.keys(versions).sort(semver.compareLoose)
141-
if (!args.length)
142-
args = ['']
143228

144229
// remove readme unless we asked for it
145230
if (args.indexOf('readme') === -1)
@@ -152,36 +237,22 @@ class View extends BaseCommand {
152237
if (args.indexOf('readme') !== -1)
153238
delete versions[v].readme
154239

155-
results.push(showFields(pckmnt, versions[v], arg))
240+
data.push(showFields(pckmnt, versions[v], arg))
156241
})
157242
}
158243
})
159-
let retval = results.reduce(reducer, {})
160-
161-
if (args.length === 1 && args[0] === '') {
162-
retval = cleanBlanks(retval)
163-
log.silly('view', retval)
164-
}
165244

166245
if (
167246
!this.npm.config.get('json') &&
168247
args.length === 1 &&
169248
args[0] === ''
170-
) {
171-
// general view
249+
)
172250
pckmnt.version = version
173-
await Promise.all(
174-
results.map((v) => this.prettyView(pckmnt, v[Object.keys(v)[0]]['']))
175-
)
176-
return retval
177-
} else {
178-
// view by field name
179-
await this.printData(retval, pckmnt._id)
180-
return retval
181-
}
251+
252+
return [pckmnt, data]
182253
}
183254

184-
async printData (data, name) {
255+
async jsonData (data, name) {
185256
const versions = Object.keys(data)
186257
let msg = ''
187258
let msgJson = []
@@ -233,13 +304,7 @@ class View extends BaseCommand {
233304
msg = JSON.stringify(msgJson, null, 2) + '\n'
234305
}
235306

236-
// disable the progress bar entirely, as we can't meaningfully update it if
237-
// we may have partial lines printed.
238-
log.disableProgress()
239-
240-
// only log if there is something to log
241-
if (msg !== '')
242-
console.log(msg.trim())
307+
return msg.trim()
243308
}
244309

245310
async prettyView (packument, manifest) {
@@ -375,17 +440,18 @@ function cleanBlanks (obj) {
375440
return clean
376441
}
377442

378-
function reducer (l, r) {
379-
if (r) {
380-
Object.keys(r).forEach((v) => {
381-
l[v] = l[v] || {}
382-
Object.keys(r[v]).forEach((t) => {
383-
l[v][t] = r[v][t]
443+
// takes an array of objects and merges them into one object
444+
function reducer (acc, cur) {
445+
if (cur) {
446+
Object.keys(cur).forEach((v) => {
447+
acc[v] = acc[v] || {}
448+
Object.keys(cur[v]).forEach((t) => {
449+
acc[v][t] = cur[v][t]
384450
})
385451
})
386452
}
387453

388-
return l
454+
return acc
389455
}
390456

391457
// return whatever was printed

0 commit comments

Comments
 (0)