11cb0ef41Sopenharmony_ciconst log = require('../utils/log-shim.js')
21cb0ef41Sopenharmony_ciconst semver = require('semver')
31cb0ef41Sopenharmony_ciconst pack = require('libnpmpack')
41cb0ef41Sopenharmony_ciconst libpub = require('libnpmpublish').publish
51cb0ef41Sopenharmony_ciconst runScript = require('@npmcli/run-script')
61cb0ef41Sopenharmony_ciconst pacote = require('pacote')
71cb0ef41Sopenharmony_ciconst npa = require('npm-package-arg')
81cb0ef41Sopenharmony_ciconst npmFetch = require('npm-registry-fetch')
91cb0ef41Sopenharmony_ciconst replaceInfo = require('../utils/replace-info.js')
101cb0ef41Sopenharmony_ci
111cb0ef41Sopenharmony_ciconst otplease = require('../utils/otplease.js')
121cb0ef41Sopenharmony_ciconst { getContents, logTar } = require('../utils/tar.js')
131cb0ef41Sopenharmony_ci
141cb0ef41Sopenharmony_ci// for historical reasons, publishConfig in package.json can contain ANY config
151cb0ef41Sopenharmony_ci// keys that npm supports in .npmrc files and elsewhere.  We *may* want to
161cb0ef41Sopenharmony_ci// revisit this at some point, and have a minimal set that's a SemVer-major
171cb0ef41Sopenharmony_ci// change that ought to get a RFC written on it.
181cb0ef41Sopenharmony_ciconst { flatten } = require('@npmcli/config/lib/definitions')
191cb0ef41Sopenharmony_ciconst pkgJson = require('@npmcli/package-json')
201cb0ef41Sopenharmony_ci
211cb0ef41Sopenharmony_ciconst BaseCommand = require('../base-command.js')
221cb0ef41Sopenharmony_ciclass Publish extends BaseCommand {
231cb0ef41Sopenharmony_ci  static description = 'Publish a package'
241cb0ef41Sopenharmony_ci  static name = 'publish'
251cb0ef41Sopenharmony_ci  static params = [
261cb0ef41Sopenharmony_ci    'tag',
271cb0ef41Sopenharmony_ci    'access',
281cb0ef41Sopenharmony_ci    'dry-run',
291cb0ef41Sopenharmony_ci    'otp',
301cb0ef41Sopenharmony_ci    'workspace',
311cb0ef41Sopenharmony_ci    'workspaces',
321cb0ef41Sopenharmony_ci    'include-workspace-root',
331cb0ef41Sopenharmony_ci    'provenance',
341cb0ef41Sopenharmony_ci  ]
351cb0ef41Sopenharmony_ci
361cb0ef41Sopenharmony_ci  static usage = ['<package-spec>']
371cb0ef41Sopenharmony_ci  static workspaces = true
381cb0ef41Sopenharmony_ci  static ignoreImplicitWorkspace = false
391cb0ef41Sopenharmony_ci
401cb0ef41Sopenharmony_ci  async exec (args) {
411cb0ef41Sopenharmony_ci    if (args.length === 0) {
421cb0ef41Sopenharmony_ci      args = ['.']
431cb0ef41Sopenharmony_ci    }
441cb0ef41Sopenharmony_ci    if (args.length !== 1) {
451cb0ef41Sopenharmony_ci      throw this.usageError()
461cb0ef41Sopenharmony_ci    }
471cb0ef41Sopenharmony_ci
481cb0ef41Sopenharmony_ci    log.verbose('publish', replaceInfo(args))
491cb0ef41Sopenharmony_ci
501cb0ef41Sopenharmony_ci    const unicode = this.npm.config.get('unicode')
511cb0ef41Sopenharmony_ci    const dryRun = this.npm.config.get('dry-run')
521cb0ef41Sopenharmony_ci    const json = this.npm.config.get('json')
531cb0ef41Sopenharmony_ci    const defaultTag = this.npm.config.get('tag')
541cb0ef41Sopenharmony_ci    const ignoreScripts = this.npm.config.get('ignore-scripts')
551cb0ef41Sopenharmony_ci    const { silent } = this.npm
561cb0ef41Sopenharmony_ci
571cb0ef41Sopenharmony_ci    if (semver.validRange(defaultTag)) {
581cb0ef41Sopenharmony_ci      throw new Error('Tag name must not be a valid SemVer range: ' + defaultTag.trim())
591cb0ef41Sopenharmony_ci    }
601cb0ef41Sopenharmony_ci
611cb0ef41Sopenharmony_ci    const opts = { ...this.npm.flatOptions, progress: false }
621cb0ef41Sopenharmony_ci    log.disableProgress()
631cb0ef41Sopenharmony_ci
641cb0ef41Sopenharmony_ci    // you can publish name@version, ./foo.tgz, etc.
651cb0ef41Sopenharmony_ci    // even though the default is the 'file:.' cwd.
661cb0ef41Sopenharmony_ci    const spec = npa(args[0])
671cb0ef41Sopenharmony_ci    let manifest = await this.getManifest(spec, opts)
681cb0ef41Sopenharmony_ci
691cb0ef41Sopenharmony_ci    // only run scripts for directory type publishes
701cb0ef41Sopenharmony_ci    if (spec.type === 'directory' && !ignoreScripts) {
711cb0ef41Sopenharmony_ci      await runScript({
721cb0ef41Sopenharmony_ci        event: 'prepublishOnly',
731cb0ef41Sopenharmony_ci        path: spec.fetchSpec,
741cb0ef41Sopenharmony_ci        stdio: 'inherit',
751cb0ef41Sopenharmony_ci        pkg: manifest,
761cb0ef41Sopenharmony_ci        banner: !silent,
771cb0ef41Sopenharmony_ci      })
781cb0ef41Sopenharmony_ci    }
791cb0ef41Sopenharmony_ci
801cb0ef41Sopenharmony_ci    // we pass dryRun: true to libnpmpack so it doesn't write the file to disk
811cb0ef41Sopenharmony_ci    const tarballData = await pack(spec, {
821cb0ef41Sopenharmony_ci      ...opts,
831cb0ef41Sopenharmony_ci      foregroundScripts: this.npm.config.isDefault('foreground-scripts')
841cb0ef41Sopenharmony_ci        ? true
851cb0ef41Sopenharmony_ci        : this.npm.config.get('foreground-scripts'),
861cb0ef41Sopenharmony_ci      dryRun: true,
871cb0ef41Sopenharmony_ci      prefix: this.npm.localPrefix,
881cb0ef41Sopenharmony_ci      workspaces: this.workspacePaths,
891cb0ef41Sopenharmony_ci    })
901cb0ef41Sopenharmony_ci    const pkgContents = await getContents(manifest, tarballData)
911cb0ef41Sopenharmony_ci
921cb0ef41Sopenharmony_ci    // The purpose of re-reading the manifest is in case it changed,
931cb0ef41Sopenharmony_ci    // so that we send the latest and greatest thing to the registry
941cb0ef41Sopenharmony_ci    // note that publishConfig might have changed as well!
951cb0ef41Sopenharmony_ci    manifest = await this.getManifest(spec, opts, true)
961cb0ef41Sopenharmony_ci
971cb0ef41Sopenharmony_ci    // JSON already has the package contents
981cb0ef41Sopenharmony_ci    if (!json) {
991cb0ef41Sopenharmony_ci      logTar(pkgContents, { unicode })
1001cb0ef41Sopenharmony_ci    }
1011cb0ef41Sopenharmony_ci
1021cb0ef41Sopenharmony_ci    const resolved = npa.resolve(manifest.name, manifest.version)
1031cb0ef41Sopenharmony_ci    const registry = npmFetch.pickRegistry(resolved, opts)
1041cb0ef41Sopenharmony_ci    const creds = this.npm.config.getCredentialsByURI(registry)
1051cb0ef41Sopenharmony_ci    const noCreds = !(creds.token || creds.username || creds.certfile && creds.keyfile)
1061cb0ef41Sopenharmony_ci    const outputRegistry = replaceInfo(registry)
1071cb0ef41Sopenharmony_ci
1081cb0ef41Sopenharmony_ci    if (noCreds) {
1091cb0ef41Sopenharmony_ci      const msg = `This command requires you to be logged in to ${outputRegistry}`
1101cb0ef41Sopenharmony_ci      if (dryRun) {
1111cb0ef41Sopenharmony_ci        log.warn('', `${msg} (dry-run)`)
1121cb0ef41Sopenharmony_ci      } else {
1131cb0ef41Sopenharmony_ci        throw Object.assign(new Error(msg), { code: 'ENEEDAUTH' })
1141cb0ef41Sopenharmony_ci      }
1151cb0ef41Sopenharmony_ci    }
1161cb0ef41Sopenharmony_ci
1171cb0ef41Sopenharmony_ci    const access = opts.access === null ? 'default' : opts.access
1181cb0ef41Sopenharmony_ci    let msg = `Publishing to ${outputRegistry} with tag ${defaultTag} and ${access} access`
1191cb0ef41Sopenharmony_ci    if (dryRun) {
1201cb0ef41Sopenharmony_ci      msg = `${msg} (dry-run)`
1211cb0ef41Sopenharmony_ci    }
1221cb0ef41Sopenharmony_ci
1231cb0ef41Sopenharmony_ci    log.notice('', msg)
1241cb0ef41Sopenharmony_ci
1251cb0ef41Sopenharmony_ci    if (!dryRun) {
1261cb0ef41Sopenharmony_ci      await otplease(this.npm, opts, o => libpub(manifest, tarballData, o))
1271cb0ef41Sopenharmony_ci    }
1281cb0ef41Sopenharmony_ci
1291cb0ef41Sopenharmony_ci    if (spec.type === 'directory' && !ignoreScripts) {
1301cb0ef41Sopenharmony_ci      await runScript({
1311cb0ef41Sopenharmony_ci        event: 'publish',
1321cb0ef41Sopenharmony_ci        path: spec.fetchSpec,
1331cb0ef41Sopenharmony_ci        stdio: 'inherit',
1341cb0ef41Sopenharmony_ci        pkg: manifest,
1351cb0ef41Sopenharmony_ci        banner: !silent,
1361cb0ef41Sopenharmony_ci      })
1371cb0ef41Sopenharmony_ci
1381cb0ef41Sopenharmony_ci      await runScript({
1391cb0ef41Sopenharmony_ci        event: 'postpublish',
1401cb0ef41Sopenharmony_ci        path: spec.fetchSpec,
1411cb0ef41Sopenharmony_ci        stdio: 'inherit',
1421cb0ef41Sopenharmony_ci        pkg: manifest,
1431cb0ef41Sopenharmony_ci        banner: !silent,
1441cb0ef41Sopenharmony_ci      })
1451cb0ef41Sopenharmony_ci    }
1461cb0ef41Sopenharmony_ci
1471cb0ef41Sopenharmony_ci    if (!this.suppressOutput) {
1481cb0ef41Sopenharmony_ci      if (!silent && json) {
1491cb0ef41Sopenharmony_ci        this.npm.output(JSON.stringify(pkgContents, null, 2))
1501cb0ef41Sopenharmony_ci      } else if (!silent) {
1511cb0ef41Sopenharmony_ci        this.npm.output(`+ ${pkgContents.id}`)
1521cb0ef41Sopenharmony_ci      }
1531cb0ef41Sopenharmony_ci    }
1541cb0ef41Sopenharmony_ci
1551cb0ef41Sopenharmony_ci    return pkgContents
1561cb0ef41Sopenharmony_ci  }
1571cb0ef41Sopenharmony_ci
1581cb0ef41Sopenharmony_ci  async execWorkspaces (args) {
1591cb0ef41Sopenharmony_ci    // Suppresses JSON output in publish() so we can handle it here
1601cb0ef41Sopenharmony_ci    this.suppressOutput = true
1611cb0ef41Sopenharmony_ci
1621cb0ef41Sopenharmony_ci    const results = {}
1631cb0ef41Sopenharmony_ci    const json = this.npm.config.get('json')
1641cb0ef41Sopenharmony_ci    const { silent } = this.npm
1651cb0ef41Sopenharmony_ci    await this.setWorkspaces()
1661cb0ef41Sopenharmony_ci
1671cb0ef41Sopenharmony_ci    for (const [name, workspace] of this.workspaces.entries()) {
1681cb0ef41Sopenharmony_ci      let pkgContents
1691cb0ef41Sopenharmony_ci      try {
1701cb0ef41Sopenharmony_ci        pkgContents = await this.exec([workspace])
1711cb0ef41Sopenharmony_ci      } catch (err) {
1721cb0ef41Sopenharmony_ci        if (err.code === 'EPRIVATE') {
1731cb0ef41Sopenharmony_ci          log.warn(
1741cb0ef41Sopenharmony_ci            'publish',
1751cb0ef41Sopenharmony_ci            `Skipping workspace ${
1761cb0ef41Sopenharmony_ci              this.npm.chalk.green(name)
1771cb0ef41Sopenharmony_ci            }, marked as ${
1781cb0ef41Sopenharmony_ci              this.npm.chalk.bold('private')
1791cb0ef41Sopenharmony_ci            }`
1801cb0ef41Sopenharmony_ci          )
1811cb0ef41Sopenharmony_ci          continue
1821cb0ef41Sopenharmony_ci        }
1831cb0ef41Sopenharmony_ci        throw err
1841cb0ef41Sopenharmony_ci      }
1851cb0ef41Sopenharmony_ci      // This needs to be in-line w/ the rest of the output that non-JSON
1861cb0ef41Sopenharmony_ci      // publish generates
1871cb0ef41Sopenharmony_ci      if (!silent && !json) {
1881cb0ef41Sopenharmony_ci        this.npm.output(`+ ${pkgContents.id}`)
1891cb0ef41Sopenharmony_ci      } else {
1901cb0ef41Sopenharmony_ci        results[name] = pkgContents
1911cb0ef41Sopenharmony_ci      }
1921cb0ef41Sopenharmony_ci    }
1931cb0ef41Sopenharmony_ci
1941cb0ef41Sopenharmony_ci    if (!silent && json) {
1951cb0ef41Sopenharmony_ci      this.npm.output(JSON.stringify(results, null, 2))
1961cb0ef41Sopenharmony_ci    }
1971cb0ef41Sopenharmony_ci  }
1981cb0ef41Sopenharmony_ci
1991cb0ef41Sopenharmony_ci  // if it's a directory, read it from the file system
2001cb0ef41Sopenharmony_ci  // otherwise, get the full metadata from whatever it is
2011cb0ef41Sopenharmony_ci  // XXX can't pacote read the manifest from a directory?
2021cb0ef41Sopenharmony_ci  async getManifest (spec, opts, logWarnings = false) {
2031cb0ef41Sopenharmony_ci    let manifest
2041cb0ef41Sopenharmony_ci    if (spec.type === 'directory') {
2051cb0ef41Sopenharmony_ci      const changes = []
2061cb0ef41Sopenharmony_ci      const pkg = await pkgJson.fix(spec.fetchSpec, { changes })
2071cb0ef41Sopenharmony_ci      if (changes.length && logWarnings) {
2081cb0ef41Sopenharmony_ci        /* eslint-disable-next-line max-len */
2091cb0ef41Sopenharmony_ci        log.warn('publish', 'npm auto-corrected some errors in your package.json when publishing.  Please run "npm pkg fix" to address these errors.')
2101cb0ef41Sopenharmony_ci        log.warn('publish', `errors corrected:\n${changes.join('\n')}`)
2111cb0ef41Sopenharmony_ci      }
2121cb0ef41Sopenharmony_ci      // Prepare is the special function for publishing, different than normalize
2131cb0ef41Sopenharmony_ci      const { content } = await pkg.prepare()
2141cb0ef41Sopenharmony_ci      manifest = content
2151cb0ef41Sopenharmony_ci    } else {
2161cb0ef41Sopenharmony_ci      manifest = await pacote.manifest(spec, {
2171cb0ef41Sopenharmony_ci        ...opts,
2181cb0ef41Sopenharmony_ci        fullmetadata: true,
2191cb0ef41Sopenharmony_ci        fullReadJson: true,
2201cb0ef41Sopenharmony_ci      })
2211cb0ef41Sopenharmony_ci    }
2221cb0ef41Sopenharmony_ci    if (manifest.publishConfig) {
2231cb0ef41Sopenharmony_ci      flatten(manifest.publishConfig, opts)
2241cb0ef41Sopenharmony_ci    }
2251cb0ef41Sopenharmony_ci    return manifest
2261cb0ef41Sopenharmony_ci  }
2271cb0ef41Sopenharmony_ci}
2281cb0ef41Sopenharmony_cimodule.exports = Publish
229