diff --git a/lib/commands/view.js b/lib/commands/view.js index c0d5bf552eee0..155288c96ea85 100644 --- a/lib/commands/view.js +++ b/lib/commands/view.js @@ -175,6 +175,7 @@ class View extends BaseCommand { } async getData (pkg, args) { + const json = this.npm.config.get('json') const opts = { ...this.npm.flatOptions, preferOnline: true, @@ -228,7 +229,12 @@ class View extends BaseCommand { delete versions[v].readme } - data.push(showFields(pckmnt, versions[v], arg)) + data.push(showFields({ + data: pckmnt, + version: versions[v], + fields: arg, + json, + })) }) } }) @@ -242,11 +248,7 @@ class View extends BaseCommand { throw er } - if ( - !this.npm.config.get('json') && - args.length === 1 && - args[0] === '' - ) { + if (!json && args.length === 1 && args[0] === '') { pckmnt.version = version } @@ -432,7 +434,7 @@ function reducer (acc, cur) { } // return whatever was printed -function showFields (data, version, fields) { +function showFields ({ data, version, fields, json }) { const o = {} ;[data, version].forEach((s) => { Object.keys(s).forEach((k) => { @@ -441,7 +443,7 @@ function showFields (data, version, fields) { }) const queryable = new Queryable(o) - const s = queryable.query(fields) + const s = queryable.query(fields, { unwrapSingleItemArrays: !json }) const res = { [version.version]: s } if (s) { diff --git a/lib/utils/queryable.js b/lib/utils/queryable.js index 69621d928e8dd..372cde91e1ce0 100644 --- a/lib/utils/queryable.js +++ b/lib/utils/queryable.js @@ -83,7 +83,7 @@ const parseKeys = key => { return res } -const getter = ({ data, key }) => { +const getter = ({ data, key }, { unwrapSingleItemArrays = true } = {}) => { // keys are a list in which each entry represents the name of // a property that should be walked through the object in order to // return the final found value @@ -122,7 +122,7 @@ const getter = ({ data, key }) => { // these are some legacy expectations from // the old API consumed by lib/view.js - if (Array.isArray(_data) && _data.length <= 1) { + if (unwrapSingleItemArrays && Array.isArray(_data) && _data.length <= 1) { _data = _data[0] } @@ -243,7 +243,7 @@ class Queryable { this.#data = obj } - query (queries) { + query (queries, opts) { // this ugly interface here is meant to be a compatibility layer // with the legacy API lib/view.js is consuming, if at some point // we refactor that command then we can revisit making this nicer @@ -255,7 +255,7 @@ class Queryable { getter({ data: this.#data, key: query, - }) + }, opts) if (Array.isArray(queries)) { let res = {} diff --git a/tap-snapshots/test/lib/commands/view.js.test.cjs b/tap-snapshots/test/lib/commands/view.js.test.cjs index 3e06ecf5d054e..bf20ed4679a31 100644 --- a/tap-snapshots/test/lib/commands/view.js.test.cjs +++ b/tap-snapshots/test/lib/commands/view.js.test.cjs @@ -300,6 +300,28 @@ dist-tags: published over a year from now ` +exports[`test/lib/commands/view.js TAP package with single version full json > must match snapshot 1`] = ` +{ + "_id": "single-version", + "name": "single-version", + "dist-tags": { + "latest": "1.0.0" + }, + "time": { + "1.0.0": "2024-05-07T19:41:40.177Z" + }, + "versions": [ + "1.0.0" + ], + "version": "1.0.0", + "dist": { + "shasum": "123", + "tarball": "http://hm.single-version.com/1.0.0.tgz", + "fileCount": 1 + } +} +` + exports[`test/lib/commands/view.js TAP specific field names array field - 1 element > must match snapshot 1`] = ` claudia ` diff --git a/test/lib/commands/view.js b/test/lib/commands/view.js index c50668791bbe5..2b60cf9442d19 100644 --- a/test/lib/commands/view.js +++ b/test/lib/commands/view.js @@ -252,6 +252,27 @@ const packument = (nv, opts) => { }, }, }, + 'single-version': { + _id: 'single-version', + name: 'single-version', + 'dist-tags': { + latest: '1.0.0', + }, + time: { + '1.0.0': yesterday, + }, + versions: { + '1.0.0': { + name: 'single-version', + version: '1.0.0', + dist: { + shasum: '123', + tarball: 'http://hm.single-version.com/1.0.0.tgz', + fileCount: 1, + }, + }, + }, + }, } if (nv.type === 'git') { return mocks[nv.hosted.project] @@ -357,6 +378,27 @@ t.test('package with --json and no versions', async t => { t.equal(joinedOutput(), '', 'no info to display') }) +t.test('package with single version', async t => { + t.test('full json', async t => { + const { view, joinedOutput } = await loadMockNpm(t, { config: { json: true } }) + await view.exec(['single-version']) + t.matchSnapshot(joinedOutput()) + }) + + t.test('json and versions arg', async t => { + const { view, joinedOutput } = await loadMockNpm(t, { config: { json: true } }) + await view.exec(['single-version', 'versions']) + const parsed = JSON.parse(joinedOutput()) + t.strictSame(parsed, ['1.0.0'], 'does not unwrap single item arrays in json') + }) + + t.test('no json and versions arg', async t => { + const { view, joinedOutput } = await loadMockNpm(t, { config: { json: false } }) + await view.exec(['single-version', 'versions']) + t.strictSame(joinedOutput(), '1.0.0', 'unwraps single item arrays in basic mode') + }) +}) + t.test('package in cwd', async t => { const prefixDir = { 'package.json': JSON.stringify({