All files / lib publish.js

100% Statements 64/64
100% Branches 26/26
100% Functions 7/7
100% Lines 61/61

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128    7x 7x 7x 7x 7x 7x 7x 7x   7x 7x 7x 7x         7x   7x 7x 7x         7x   7x 7x 1x 7x 1x   6x   6x 6x 6x 1x   5x 5x 5x 3x 2x 1x   5x         7x 8x             7x 7x     2x   7x 5x     5x 5x   5x 1x     5x 4x               5x 5x       5x 2x   5x       3x 3x 1x 3x     5x 4x             4x               5x     7x  
'use strict'
 
const util = require('util')
const log = require('npmlog')
const semver = require('semver')
const pack = require('libnpmpack')
const libpub = require('libnpmpublish').publish
const runScript = require('@npmcli/run-script')
const pacote = require('pacote')
const npa = require('npm-package-arg')
 
const npm = require('./npm.js')
const output = require('./utils/output.js')
const otplease = require('./utils/otplease.js')
const { getContents, logTar } = require('./utils/tar.js')
 
// this is the only case in the CLI where we use the old full slow
// 'read-package-json' module, because we want to pull in all the
// defaults and metadata, like git sha's and default scripts and all that.
const readJson = util.promisify(require('read-package-json'))
 
const completion = require('./utils/completion/none.js')
const usageUtil = require('./utils/usage.js')
const usage = usageUtil('publish',
  'npm publish [<folder>] [--tag <tag>] [--access <public|restricted>] [--dry-run]' +
  '\n\nPublishes \'.\' if no argument supplied' +
  '\nSets tag `latest` if no --tag specified')
 
const cmd = (args, cb) => publish(args).then(() => cb()).catch(cb)
 
const publish = async args => {
  if (args.length === 0)
    args = ['.']
  if (args.length !== 1)
    throw usage
 
  log.verbose('publish', args)
 
  const opts = { ...npm.flatOptions }
  const { json, defaultTag } = opts
  if (semver.validRange(defaultTag))
    throw new Error('Tag name must not be a valid SemVer range: ' + defaultTag.trim())
 
  const tarball = await publish_(args[0], opts)
  const silent = log.level === 'silent'
  if (!silent && json)
    output(JSON.stringify(tarball, null, 2))
  else if (!silent)
    output(`+ ${tarball.id}`)
 
  return tarball
}
 
// if it's a directory, read it from the file system
// otherwise, get the full metadata from whatever it is
const getManifest = (spec, opts) =>
  spec.type === 'directory' ? readJson(`${spec.fetchSpec}/package.json`)
  : pacote.manifest(spec, { ...opts, fullMetadata: true })
 
// for historical reasons, publishConfig in package.json can contain
// ANY config keys that npm supports in .npmrc files and elsewhere.
// We *may* want to revisit this at some point, and have a minimal set
// that's a SemVer-major change that ought to get a RFC written on it.
const { flatten } = require('./utils/flat-options.js')
const publishConfigToOpts = publishConfig =>
  // create a new object that inherits from the config stack
  // then squash the css-case into camelCase opts, like we do
  flatten(Object.assign(Object.create(npm.config.list[0]), publishConfig))
 
const publish_ = async (arg, opts) => {
  const { unicode, dryRun, json } = opts
  // you can publish name@version, ./foo.tgz, etc.
  // even though the default is the 'file:.' cwd.
  const spec = npa(arg)
  const manifest = await getManifest(spec, opts)
 
  if (manifest.publishConfig)
    Object.assign(opts, publishConfigToOpts(manifest.publishConfig))
 
  // only run scripts for directory type publishes
  if (spec.type === 'directory') {
    await runScript({
      event: 'prepublishOnly',
      path: spec.fetchSpec,
      stdio: 'inherit',
      pkg: manifest,
    })
  }
 
  const tarballData = await pack(spec, opts)
  const pkgContents = await getContents(manifest, tarballData)
 
  // note that logTar calls npmlog.notice(), so if we ARE in silent mode,
  // this will do nothing, but we still want it in the debuglog if it fails.
  if (!json)
    logTar(pkgContents, { log, unicode })
 
  if (!dryRun) {
    // The purpose of re-reading the manifest is in case it changed,
    // so that we send the latest and greatest thing to the registry
    // note that publishConfig might have changed as well!
    const manifest = await getManifest(spec, opts)
    if (manifest.publishConfig)
      Object.assign(opts, publishConfigToOpts(manifest.publishConfig))
    await otplease(opts, opts => libpub(manifest, tarballData, opts))
  }
 
  if (spec.type === 'directory') {
    await runScript({
      event: 'publish',
      path: spec.fetchSpec,
      stdio: 'inherit',
      pkg: manifest,
    })
 
    await runScript({
      event: 'postpublish',
      path: spec.fetchSpec,
      stdio: 'inherit',
      pkg: manifest,
    })
  }
 
  return pkgContents
}
 
module.exports = Object.assign(cmd, { usage, completion })