11cb0ef41Sopenharmony_ciconst libaccess = require('libnpmaccess')
21cb0ef41Sopenharmony_ciconst libunpub = require('libnpmpublish').unpublish
31cb0ef41Sopenharmony_ciconst npa = require('npm-package-arg')
41cb0ef41Sopenharmony_ciconst pacote = require('pacote')
51cb0ef41Sopenharmony_ciconst pkgJson = require('@npmcli/package-json')
61cb0ef41Sopenharmony_ci
71cb0ef41Sopenharmony_ciconst { flatten } = require('@npmcli/config/lib/definitions')
81cb0ef41Sopenharmony_ciconst getIdentity = require('../utils/get-identity.js')
91cb0ef41Sopenharmony_ciconst log = require('../utils/log-shim')
101cb0ef41Sopenharmony_ciconst otplease = require('../utils/otplease.js')
111cb0ef41Sopenharmony_ci
121cb0ef41Sopenharmony_ciconst LAST_REMAINING_VERSION_ERROR = 'Refusing to delete the last version of the package. ' +
131cb0ef41Sopenharmony_ci'It will block from republishing a new version for 24 hours.\n' +
141cb0ef41Sopenharmony_ci'Run with --force to do this.'
151cb0ef41Sopenharmony_ci
161cb0ef41Sopenharmony_ciconst BaseCommand = require('../base-command.js')
171cb0ef41Sopenharmony_ciclass Unpublish extends BaseCommand {
181cb0ef41Sopenharmony_ci  static description = 'Remove a package from the registry'
191cb0ef41Sopenharmony_ci  static name = 'unpublish'
201cb0ef41Sopenharmony_ci  static params = ['dry-run', 'force', 'workspace', 'workspaces']
211cb0ef41Sopenharmony_ci  static usage = ['[<package-spec>]']
221cb0ef41Sopenharmony_ci  static workspaces = true
231cb0ef41Sopenharmony_ci  static ignoreImplicitWorkspace = false
241cb0ef41Sopenharmony_ci
251cb0ef41Sopenharmony_ci  static async getKeysOfVersions (name, opts) {
261cb0ef41Sopenharmony_ci    const packument = await pacote.packument(name, {
271cb0ef41Sopenharmony_ci      ...opts,
281cb0ef41Sopenharmony_ci      spec: name,
291cb0ef41Sopenharmony_ci      query: { write: true },
301cb0ef41Sopenharmony_ci    })
311cb0ef41Sopenharmony_ci    return Object.keys(packument.versions)
321cb0ef41Sopenharmony_ci  }
331cb0ef41Sopenharmony_ci
341cb0ef41Sopenharmony_ci  static async completion (args, npm) {
351cb0ef41Sopenharmony_ci    const { partialWord, conf } = args
361cb0ef41Sopenharmony_ci
371cb0ef41Sopenharmony_ci    if (conf.argv.remain.length >= 3) {
381cb0ef41Sopenharmony_ci      return []
391cb0ef41Sopenharmony_ci    }
401cb0ef41Sopenharmony_ci
411cb0ef41Sopenharmony_ci    const opts = { ...npm.flatOptions }
421cb0ef41Sopenharmony_ci    const username = await getIdentity(npm, { ...opts }).catch(() => null)
431cb0ef41Sopenharmony_ci    if (!username) {
441cb0ef41Sopenharmony_ci      return []
451cb0ef41Sopenharmony_ci    }
461cb0ef41Sopenharmony_ci
471cb0ef41Sopenharmony_ci    const access = await libaccess.getPackages(username, opts)
481cb0ef41Sopenharmony_ci    // do a bit of filtering at this point, so that we don't need
491cb0ef41Sopenharmony_ci    // to fetch versions for more than one thing, but also don't
501cb0ef41Sopenharmony_ci    // accidentally unpublish a whole project
511cb0ef41Sopenharmony_ci    let pkgs = Object.keys(access)
521cb0ef41Sopenharmony_ci    if (!partialWord || !pkgs.length) {
531cb0ef41Sopenharmony_ci      return pkgs
541cb0ef41Sopenharmony_ci    }
551cb0ef41Sopenharmony_ci
561cb0ef41Sopenharmony_ci    const pp = npa(partialWord).name
571cb0ef41Sopenharmony_ci    pkgs = pkgs.filter(p => !p.indexOf(pp))
581cb0ef41Sopenharmony_ci    if (pkgs.length > 1) {
591cb0ef41Sopenharmony_ci      return pkgs
601cb0ef41Sopenharmony_ci    }
611cb0ef41Sopenharmony_ci
621cb0ef41Sopenharmony_ci    const versions = await Unpublish.getKeysOfVersions(pkgs[0], opts)
631cb0ef41Sopenharmony_ci    if (!versions.length) {
641cb0ef41Sopenharmony_ci      return pkgs
651cb0ef41Sopenharmony_ci    } else {
661cb0ef41Sopenharmony_ci      return versions.map(v => `${pkgs[0]}@${v}`)
671cb0ef41Sopenharmony_ci    }
681cb0ef41Sopenharmony_ci  }
691cb0ef41Sopenharmony_ci
701cb0ef41Sopenharmony_ci  async exec (args, { localPrefix } = {}) {
711cb0ef41Sopenharmony_ci    if (args.length > 1) {
721cb0ef41Sopenharmony_ci      throw this.usageError()
731cb0ef41Sopenharmony_ci    }
741cb0ef41Sopenharmony_ci
751cb0ef41Sopenharmony_ci    // workspace mode
761cb0ef41Sopenharmony_ci    if (!localPrefix) {
771cb0ef41Sopenharmony_ci      localPrefix = this.npm.localPrefix
781cb0ef41Sopenharmony_ci    }
791cb0ef41Sopenharmony_ci
801cb0ef41Sopenharmony_ci    const force = this.npm.config.get('force')
811cb0ef41Sopenharmony_ci    const { silent } = this.npm
821cb0ef41Sopenharmony_ci    const dryRun = this.npm.config.get('dry-run')
831cb0ef41Sopenharmony_ci
841cb0ef41Sopenharmony_ci    let spec
851cb0ef41Sopenharmony_ci    if (args.length) {
861cb0ef41Sopenharmony_ci      spec = npa(args[0])
871cb0ef41Sopenharmony_ci      if (spec.type !== 'version' && spec.rawSpec !== '*') {
881cb0ef41Sopenharmony_ci        throw this.usageError(
891cb0ef41Sopenharmony_ci          'Can only unpublish a single version, or the entire project.\n' +
901cb0ef41Sopenharmony_ci          'Tags and ranges are not supported.'
911cb0ef41Sopenharmony_ci        )
921cb0ef41Sopenharmony_ci      }
931cb0ef41Sopenharmony_ci    }
941cb0ef41Sopenharmony_ci
951cb0ef41Sopenharmony_ci    log.silly('unpublish', 'args[0]', args[0])
961cb0ef41Sopenharmony_ci    log.silly('unpublish', 'spec', spec)
971cb0ef41Sopenharmony_ci
981cb0ef41Sopenharmony_ci    if (spec?.rawSpec === '*' && !force) {
991cb0ef41Sopenharmony_ci      throw this.usageError(
1001cb0ef41Sopenharmony_ci        'Refusing to delete entire project.\n' +
1011cb0ef41Sopenharmony_ci        'Run with --force to do this.'
1021cb0ef41Sopenharmony_ci      )
1031cb0ef41Sopenharmony_ci    }
1041cb0ef41Sopenharmony_ci
1051cb0ef41Sopenharmony_ci    const opts = { ...this.npm.flatOptions }
1061cb0ef41Sopenharmony_ci
1071cb0ef41Sopenharmony_ci    let manifest
1081cb0ef41Sopenharmony_ci    try {
1091cb0ef41Sopenharmony_ci      const { content } = await pkgJson.prepare(localPrefix)
1101cb0ef41Sopenharmony_ci      manifest = content
1111cb0ef41Sopenharmony_ci    } catch (err) {
1121cb0ef41Sopenharmony_ci      if (err.code === 'ENOENT' || err.code === 'ENOTDIR') {
1131cb0ef41Sopenharmony_ci        if (!spec) {
1141cb0ef41Sopenharmony_ci          // We needed a local package.json to figure out what package to
1151cb0ef41Sopenharmony_ci          // unpublish
1161cb0ef41Sopenharmony_ci          throw this.usageError()
1171cb0ef41Sopenharmony_ci        }
1181cb0ef41Sopenharmony_ci      } else {
1191cb0ef41Sopenharmony_ci        // folks should know if ANY local package.json had a parsing error.
1201cb0ef41Sopenharmony_ci        // They may be relying on `publishConfig` to be loading and we don't
1211cb0ef41Sopenharmony_ci        // want to ignore errors in that case.
1221cb0ef41Sopenharmony_ci        throw err
1231cb0ef41Sopenharmony_ci      }
1241cb0ef41Sopenharmony_ci    }
1251cb0ef41Sopenharmony_ci
1261cb0ef41Sopenharmony_ci    let pkgVersion // for cli output
1271cb0ef41Sopenharmony_ci    if (spec) {
1281cb0ef41Sopenharmony_ci      pkgVersion = spec.type === 'version' ? `@${spec.rawSpec}` : ''
1291cb0ef41Sopenharmony_ci    } else {
1301cb0ef41Sopenharmony_ci      spec = npa.resolve(manifest.name, manifest.version)
1311cb0ef41Sopenharmony_ci      log.verbose('unpublish', manifest)
1321cb0ef41Sopenharmony_ci      pkgVersion = manifest.version ? `@${manifest.version}` : ''
1331cb0ef41Sopenharmony_ci      if (!manifest.version && !force) {
1341cb0ef41Sopenharmony_ci        throw this.usageError(
1351cb0ef41Sopenharmony_ci          'Refusing to delete entire project.\n' +
1361cb0ef41Sopenharmony_ci          'Run with --force to do this.'
1371cb0ef41Sopenharmony_ci        )
1381cb0ef41Sopenharmony_ci      }
1391cb0ef41Sopenharmony_ci    }
1401cb0ef41Sopenharmony_ci
1411cb0ef41Sopenharmony_ci    // If localPrefix has a package.json with a name that matches the package
1421cb0ef41Sopenharmony_ci    // being unpublished, load up the publishConfig
1431cb0ef41Sopenharmony_ci    if (manifest?.name === spec.name && manifest.publishConfig) {
1441cb0ef41Sopenharmony_ci      flatten(manifest.publishConfig, opts)
1451cb0ef41Sopenharmony_ci    }
1461cb0ef41Sopenharmony_ci
1471cb0ef41Sopenharmony_ci    const versions = await Unpublish.getKeysOfVersions(spec.name, opts)
1481cb0ef41Sopenharmony_ci    if (versions.length === 1 && spec.rawSpec === versions[0] && !force) {
1491cb0ef41Sopenharmony_ci      throw this.usageError(LAST_REMAINING_VERSION_ERROR)
1501cb0ef41Sopenharmony_ci    }
1511cb0ef41Sopenharmony_ci    if (versions.length === 1) {
1521cb0ef41Sopenharmony_ci      pkgVersion = ''
1531cb0ef41Sopenharmony_ci    }
1541cb0ef41Sopenharmony_ci
1551cb0ef41Sopenharmony_ci    if (!dryRun) {
1561cb0ef41Sopenharmony_ci      await otplease(this.npm, opts, o => libunpub(spec, o))
1571cb0ef41Sopenharmony_ci    }
1581cb0ef41Sopenharmony_ci    if (!silent) {
1591cb0ef41Sopenharmony_ci      this.npm.output(`- ${spec.name}${pkgVersion}`)
1601cb0ef41Sopenharmony_ci    }
1611cb0ef41Sopenharmony_ci  }
1621cb0ef41Sopenharmony_ci
1631cb0ef41Sopenharmony_ci  async execWorkspaces (args) {
1641cb0ef41Sopenharmony_ci    await this.setWorkspaces()
1651cb0ef41Sopenharmony_ci
1661cb0ef41Sopenharmony_ci    for (const path of this.workspacePaths) {
1671cb0ef41Sopenharmony_ci      await this.exec(args, { localPrefix: path })
1681cb0ef41Sopenharmony_ci    }
1691cb0ef41Sopenharmony_ci  }
1701cb0ef41Sopenharmony_ci}
1711cb0ef41Sopenharmony_cimodule.exports = Unpublish
172