diff --git a/docs/content/commands/npm-pack.md b/docs/content/commands/npm-pack.md index 8652cba6852f7..ff90bd74472b4 100644 --- a/docs/content/commands/npm-pack.md +++ b/docs/content/commands/npm-pack.md @@ -7,7 +7,7 @@ description: Create a tarball from a package ### Synopsis ```bash -npm pack [[<@scope>/]...] [--dry-run] +npm pack [[<@scope>/]...] [--dry-run] [--json] ``` ### Configuration diff --git a/lib/pack.js b/lib/pack.js index 5c0da6be7b6e0..52d4c3e7f900a 100644 --- a/lib/pack.js +++ b/lib/pack.js @@ -24,7 +24,7 @@ class Pack extends BaseCommand { /* istanbul ignore next - see test/lib/load-all-commands.js */ static get params () { - return ['dry-run', 'workspace', 'workspaces'] + return ['dry-run', 'json', 'workspace', 'workspaces'] } /* istanbul ignore next - see test/lib/load-all-commands.js */ @@ -46,6 +46,7 @@ class Pack extends BaseCommand { const unicode = this.npm.config.get('unicode') const dryRun = this.npm.config.get('dry-run') + const json = this.npm.config.get('json') // Get the manifests and filenames first so we can bail early on manifest // errors before making any tarballs @@ -74,6 +75,11 @@ class Pack extends BaseCommand { tarballs.push(pkgContents) } + if (json) { + this.npm.output(JSON.stringify(tarballs, null, 2)) + return + } + for (const tar of tarballs) { logTar(tar, { log, unicode }) this.npm.output(tar.filename.replace(/^@/, '').replace(/\//, '-')) diff --git a/tap-snapshots/test/lib/load-all-commands.js.test.cjs b/tap-snapshots/test/lib/load-all-commands.js.test.cjs index c48745d67ec69..c549c057c3761 100644 --- a/tap-snapshots/test/lib/load-all-commands.js.test.cjs +++ b/tap-snapshots/test/lib/load-all-commands.js.test.cjs @@ -653,7 +653,7 @@ Usage: npm pack [[<@scope>/]...] Options: -[--dry-run] +[--dry-run] [--json] [-w|--workspace [-w|--workspace ...]] [-ws|--workspaces] diff --git a/tap-snapshots/test/lib/utils/npm-usage.js.test.cjs b/tap-snapshots/test/lib/utils/npm-usage.js.test.cjs index 5fb7a871525e8..a67819eb05bdc 100644 --- a/tap-snapshots/test/lib/utils/npm-usage.js.test.cjs +++ b/tap-snapshots/test/lib/utils/npm-usage.js.test.cjs @@ -740,7 +740,7 @@ All commands: npm pack [[<@scope>/]...] Options: - [--dry-run] + [--dry-run] [--json] [-w|--workspace [-w|--workspace ...]] [-ws|--workspaces] diff --git a/test/lib/pack.js b/test/lib/pack.js index 6706042b4c82e..ff7bef19d383a 100644 --- a/test/lib/pack.js +++ b/test/lib/pack.js @@ -73,7 +73,7 @@ t.test('should pack given directory', (t) => { const npm = mockNpm({ config: { unicode: true, - json: true, + json: false, 'dry-run': true, }, output, @@ -108,7 +108,7 @@ t.test('should pack given directory for scoped package', (t) => { const npm = mockNpm({ config: { unicode: true, - json: true, + json: false, 'dry-run': true, }, output, @@ -158,6 +158,93 @@ t.test('should log pack contents', (t) => { }) }) +t.test('should log output as valid json', (t) => { + const testDir = t.testdir({ + 'package.json': JSON.stringify({ + name: 'my-cool-pkg', + version: '1.0.0', + main: './index.js', + }, null, 2), + 'README.md': 'text', + 'index.js': 'void', + }) + + const Pack = t.mock('../../lib/pack.js', { + libnpmpack, + '../../lib/utils/tar.js': { + getContents: async () => ({ + id: '@ruyadorno/redact@1.0.0', + name: '@ruyadorno/redact', + version: '1.0.0', + size: 2450, + unpackedSize: 4911, + shasum: '044c7574639b923076069d6e801e2d1866430f17', + // mocks exactly how ssri Integrity works: + integrity: { + sha512: [ + { + source: 'sha512-JSdyskeR2qonBUaQ4vdlU/vQGSfgCxSq5O+vH+d2yVWRqzso4O3gUzd6QX/V7OWV//zU7kA5o63Zf433jUnOtQ==', + digest: 'JSdyskeR2qonBUaQ4vdlU/vQGSfgCxSq5O+vH+d2yVWRqzso4O3gUzd6QX/V7OWV//zU7kA5o63Zf433jUnOtQ==', + algorithm: 'sha512', + options: [], + }, + ], + toJSON () { + return 'sha512-JSdyskeR2qonBUaQ4vdlU/vQGSfgCxSq5O+vH+d2yVWRqzso4O3gUzd6QX/V7OWV//zU7kA5o63Zf433jUnOtQ==' + }, + }, + filename: '@ruyadorno/redact-1.0.0.tgz', + files: [ + { path: 'LICENSE', size: 1113, mode: 420 }, + { path: 'README.md', size: 2639, mode: 420 }, + { path: 'index.js', size: 719, mode: 493 }, + { path: 'package.json', size: 440, mode: 420 }, + ], + entryCount: 4, + bundled: [], + }), + }, + npmlog: { + notice: () => {}, + showProgress: () => {}, + clearProgress: () => {}, + }, + }) + const npm = mockNpm({ + config: { + unicode: true, + json: true, + 'dry-run': true, + }, + output, + }) + const pack = new Pack(npm) + + pack.exec([testDir], err => { + t.error(err, { bail: true }) + + t.match(JSON.parse(OUTPUT), [{ + id: '@ruyadorno/redact@1.0.0', + name: '@ruyadorno/redact', + version: '1.0.0', + size: 2450, + unpackedSize: 4911, + shasum: '044c7574639b923076069d6e801e2d1866430f17', + integrity: 'sha512-JSdyskeR2qonBUaQ4vdlU/vQGSfgCxSq5O+vH+d2yVWRqzso4O3gUzd6QX/V7OWV//zU7kA5o63Zf433jUnOtQ==', + filename: '@ruyadorno/redact-1.0.0.tgz', + files: [ + { path: 'LICENSE' }, + { path: 'README.md' }, + { path: 'index.js' }, + { path: 'package.json' }, + ], + entryCount: 4, + }], 'pack details output as valid json') + + t.end() + }) +}) + t.test('invalid packument', (t) => { const mockPacote = { manifest: () => { @@ -176,7 +263,7 @@ t.test('invalid packument', (t) => { const npm = mockNpm({ config: { unicode: true, - json: true, + json: false, 'dry-run': true, }, output,