diff --git a/lib/commands/pack.js b/lib/commands/pack.js index 0719fa3b85828..74c29699a05c9 100644 --- a/lib/commands/pack.js +++ b/lib/commands/pack.js @@ -1,11 +1,8 @@ -const util = require('util') const pacote = require('pacote') const libpack = require('libnpmpack') const npa = require('npm-package-arg') -const path = require('path') const log = require('../utils/log-shim') const { getContents, logTar } = require('../utils/tar.js') -const writeFile = util.promisify(require('fs').writeFile) const BaseCommand = require('../base-command.js') class Pack extends BaseCommand { @@ -28,7 +25,6 @@ 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 @@ -40,24 +36,15 @@ class Pack extends BaseCommand { if (!manifest._id) { throw new Error('Invalid package, must have name and version') } - - const filename = `${manifest.name}-${manifest.version}.tgz` - .replace(/^@/, '').replace(/\//, '-') - manifests.push({ arg, filename, manifest }) + manifests.push({ arg, manifest }) } // Load tarball names up for printing afterward to isolate from the // noise generated during packing const tarballs = [] - for (const { arg, filename, manifest } of manifests) { + for (const { arg, manifest } of manifests) { const tarballData = await libpack(arg, this.npm.flatOptions) const pkgContents = await getContents(manifest, tarballData) - const tarballFilename = path.resolve(this.npm.config.get('pack-destination'), filename) - - if (!dryRun) { - await writeFile(tarballFilename, tarballData) - } - tarballs.push(pkgContents) } diff --git a/lib/commands/publish.js b/lib/commands/publish.js index 339c80daad65e..63106c520bd58 100644 --- a/lib/commands/publish.js +++ b/lib/commands/publish.js @@ -83,7 +83,8 @@ class Publish extends BaseCommand { }) } - const tarballData = await pack(spec, opts) + // we pass dryRun: true to libnpmpack so it doesn't write the file to disk + const tarballData = await pack(spec, { ...opts, dryRun: true }) const pkgContents = await getContents(manifest, tarballData) // The purpose of re-reading the manifest is in case it changed, diff --git a/lib/utils/config/definitions.js b/lib/utils/config/definitions.js index 79222881c9734..fee91eeeac632 100644 --- a/lib/utils/config/definitions.js +++ b/lib/utils/config/definitions.js @@ -1455,6 +1455,7 @@ define('pack-destination', { description: ` Directory in which \`npm pack\` will save tarballs. `, + flatten, }) define('parseable', { diff --git a/workspaces/libnpmpack/lib/index.js b/workspaces/libnpmpack/lib/index.js index 23bb9df4b2247..a2c95cf938dcf 100644 --- a/workspaces/libnpmpack/lib/index.js +++ b/workspaces/libnpmpack/lib/index.js @@ -3,6 +3,9 @@ const pacote = require('pacote') const npa = require('npm-package-arg') const runScript = require('@npmcli/run-script') +const path = require('path') +const util = require('util') +const writeFile = util.promisify(require('fs').writeFile) module.exports = pack async function pack (spec = 'file:.', opts = {}) { @@ -33,6 +36,14 @@ async function pack (spec = 'file:.', opts = {}) { integrity: manifest._integrity, }) + // check for explicit `false` so the default behavior is to skip writing to disk + if (opts.dryRun === false) { + const filename = `${manifest.name}-${manifest.version}.tgz` + .replace(/^@/, '').replace(/\//, '-') + const destination = path.resolve(opts.packDestination, filename) + await writeFile(destination, tarball) + } + if (spec.type === 'directory') { // postpack await runScript({ diff --git a/workspaces/libnpmpack/test/index.js b/workspaces/libnpmpack/test/index.js index dcc5e41f4b509..5f25f416655fd 100644 --- a/workspaces/libnpmpack/test/index.js +++ b/workspaces/libnpmpack/test/index.js @@ -1,6 +1,8 @@ 'use strict' const t = require('tap') +const fs = require('fs') +const path = require('path') const pack = require('../lib/index.js') const tnock = require('./fixtures/tnock.js') @@ -29,6 +31,43 @@ t.test('packs from local directory', async t => { }) }) +t.test('writes tarball to file when dryRun === false', async t => { + const testDir = t.testdir({ + 'package.json': JSON.stringify({ + name: 'my-cool-pkg', + version: '1.0.0', + scripts: { + prepack: 'touch prepack', + postpack: 'touch postpack', + }, + }, null, 2), + }) + + const cwd = process.cwd() + process.chdir(testDir) + + const tarball = await pack('file:.', { + dryRun: false, + packDestination: testDir, + log: { level: 'silent' }, // so the test doesn't try to log + }) + t.ok(tarball) + const expectedTarball = path.join(testDir, 'my-cool-pkg-1.0.0.tgz') + t.ok(fs.existsSync(expectedTarball), 'file was written') + t.same(fs.readFileSync(expectedTarball), tarball, 'wrote same data that was returned') + + const prepackTimestamp = (await fs.promises.stat(path.join(testDir, 'prepack'))).mtime + const tarballTimestamp = (await fs.promises.stat(expectedTarball)).mtime + const postpackTimestamp = (await fs.promises.stat(path.join(testDir, 'postpack'))).mtime + + t.ok(prepackTimestamp < tarballTimestamp, 'prepack ran before tarball was written') + t.ok(tarballTimestamp < postpackTimestamp, 'postpack ran after tarball was written') + + t.teardown(async () => { + process.chdir(cwd) + }) +}) + t.test('packs from local directory with silent loglevel', async t => { const testDir = t.testdir({ 'package.json': JSON.stringify({