Welcome to mirror list, hosted at ThFree Co, Russian Federation.

publish.js « lib - github.com/npm/cli.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 410e911f9a29f96e9be7e0803dcfff851411c942 (plain)
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
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 npmFetch = require('npm-registry-fetch')

const flatten = require('./utils/config/flatten.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 BaseCommand = require('./base-command.js')
class Publish extends BaseCommand {
  static get description () {
    return 'Publish a package'
  }

  /* istanbul ignore next - see test/lib/load-all-commands.js */
  static get name () {
    return 'publish'
  }

  /* istanbul ignore next - see test/lib/load-all-commands.js */
  static get usage () {
    return [
      '[<folder>] [--tag <tag>] [--access <public|restricted>] [--dry-run]',
    ]
  }

  exec (args, cb) {
    this.publish(args).then(() => cb()).catch(cb)
  }

  async publish (args) {
    if (args.length === 0)
      args = ['.']
    if (args.length !== 1)
      throw this.usage

    log.verbose('publish', args)

    const unicode = this.npm.config.get('unicode')
    const dryRun = this.npm.config.get('dry-run')
    const json = this.npm.config.get('json')
    const defaultTag = this.npm.config.get('tag')

    if (semver.validRange(defaultTag))
      throw new Error('Tag name must not be a valid SemVer range: ' + defaultTag.trim())

    const opts = { ...this.npm.flatOptions }

    // you can publish name@version, ./foo.tgz, etc.
    // even though the default is the 'file:.' cwd.
    const spec = npa(args[0])
    let manifest = await this.getManifest(spec, opts)

    if (manifest.publishConfig)
      Object.assign(opts, this.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,
        banner: log.level !== 'silent',
      })
    }

    const tarballData = await pack(spec, opts)
    const pkgContents = await getContents(manifest, tarballData)

    // 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!
    manifest = await this.getManifest(spec, opts)
    if (manifest.publishConfig)
      Object.assign(opts, this.publishConfigToOpts(manifest.publishConfig))

    // 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) {
      const resolved = npa.resolve(manifest.name, manifest.version)
      const registry = npmFetch.pickRegistry(resolved, opts)
      const creds = this.npm.config.getCredentialsByURI(registry)
      if (!creds.token && !creds.username) {
        throw Object.assign(new Error('This command requires you to be logged in.'), {
          code: 'ENEEDAUTH',
        })
      }
      await otplease(opts, opts => libpub(manifest, tarballData, opts))
    }

    if (spec.type === 'directory') {
      await runScript({
        event: 'publish',
        path: spec.fetchSpec,
        stdio: 'inherit',
        pkg: manifest,
        banner: log.level !== 'silent',
      })

      await runScript({
        event: 'postpublish',
        path: spec.fetchSpec,
        stdio: 'inherit',
        pkg: manifest,
        banner: log.level !== 'silent',
      })
    }

    const silent = log.level === 'silent'
    if (!silent && json)
      this.npm.output(JSON.stringify(pkgContents, null, 2))
    else if (!silent)
      this.npm.output(`+ ${pkgContents.id}`)

    return pkgContents
  }

  // if it's a directory, read it from the file system
  // otherwise, get the full metadata from whatever it is
  getManifest (spec, opts) {
    if (spec.type === 'directory')
      return readJson(`${spec.fetchSpec}/package.json`)
    return 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.
  publishConfigToOpts (publishConfig) {
    // create a new object that inherits from the config stack
    // then squash the css-case into camelCase opts, like we do
    // this is Object.assign()'ed onto the base npm.flatOptions
    return flatten(publishConfig, {})
  }
}
module.exports = Publish