11cb0ef41Sopenharmony_ci'use strict'
21cb0ef41Sopenharmony_ci
31cb0ef41Sopenharmony_ciconst { URL } = require('url')
41cb0ef41Sopenharmony_ciconst npa = require('npm-package-arg')
51cb0ef41Sopenharmony_ciconst npmFetch = require('npm-registry-fetch')
61cb0ef41Sopenharmony_ciconst semver = require('semver')
71cb0ef41Sopenharmony_ci
81cb0ef41Sopenharmony_ci// given a tarball url and a registry url, returns just the
91cb0ef41Sopenharmony_ci// relevant pathname portion of it, so that it can be handled
101cb0ef41Sopenharmony_ci// elegantly by npm-registry-fetch which only expects pathnames
111cb0ef41Sopenharmony_ci// and handles the registry hostname via opts
121cb0ef41Sopenharmony_ciconst getPathname = (tarball, registry) => {
131cb0ef41Sopenharmony_ci  const registryUrl = new URL(registry).pathname.slice(1)
141cb0ef41Sopenharmony_ci  let tarballUrl = new URL(tarball).pathname.slice(1)
151cb0ef41Sopenharmony_ci
161cb0ef41Sopenharmony_ci  // test the tarball url to see if it starts with a possible
171cb0ef41Sopenharmony_ci  // pathname from the registry url, in that case strips that portion
181cb0ef41Sopenharmony_ci  // of it so that we only return the post-registry-url pathname
191cb0ef41Sopenharmony_ci  if (registryUrl) {
201cb0ef41Sopenharmony_ci    tarballUrl = tarballUrl.slice(registryUrl.length)
211cb0ef41Sopenharmony_ci  }
221cb0ef41Sopenharmony_ci  return tarballUrl
231cb0ef41Sopenharmony_ci}
241cb0ef41Sopenharmony_ci
251cb0ef41Sopenharmony_ciconst unpublish = async (spec, opts) => {
261cb0ef41Sopenharmony_ci  spec = npa(spec)
271cb0ef41Sopenharmony_ci  // spec is used to pick the appropriate registry/auth combo.
281cb0ef41Sopenharmony_ci  opts = {
291cb0ef41Sopenharmony_ci    force: false,
301cb0ef41Sopenharmony_ci    ...opts,
311cb0ef41Sopenharmony_ci    spec,
321cb0ef41Sopenharmony_ci  }
331cb0ef41Sopenharmony_ci
341cb0ef41Sopenharmony_ci  try {
351cb0ef41Sopenharmony_ci    const pkgUri = spec.escapedName
361cb0ef41Sopenharmony_ci    const pkg = await npmFetch.json(pkgUri, {
371cb0ef41Sopenharmony_ci      ...opts,
381cb0ef41Sopenharmony_ci      query: { write: true },
391cb0ef41Sopenharmony_ci    })
401cb0ef41Sopenharmony_ci
411cb0ef41Sopenharmony_ci    const version = spec.rawSpec
421cb0ef41Sopenharmony_ci    const allVersions = pkg.versions || {}
431cb0ef41Sopenharmony_ci    const versionData = allVersions[version]
441cb0ef41Sopenharmony_ci
451cb0ef41Sopenharmony_ci    const rawSpecs = (!spec.rawSpec || spec.rawSpec === '*')
461cb0ef41Sopenharmony_ci    const onlyVersion = Object.keys(allVersions).length === 1
471cb0ef41Sopenharmony_ci    const noVersions = !Object.keys(allVersions).length
481cb0ef41Sopenharmony_ci
491cb0ef41Sopenharmony_ci    // if missing specific version,
501cb0ef41Sopenharmony_ci    // assumed unpublished
511cb0ef41Sopenharmony_ci    if (!versionData && !rawSpecs && !noVersions) {
521cb0ef41Sopenharmony_ci      return true
531cb0ef41Sopenharmony_ci    }
541cb0ef41Sopenharmony_ci
551cb0ef41Sopenharmony_ci    // unpublish all versions of a package:
561cb0ef41Sopenharmony_ci    // - no specs supplied "npm unpublish foo"
571cb0ef41Sopenharmony_ci    // - all specs ("*") "npm unpublish foo@*"
581cb0ef41Sopenharmony_ci    // - there was only one version
591cb0ef41Sopenharmony_ci    // - has no versions field on packument
601cb0ef41Sopenharmony_ci    if (rawSpecs || onlyVersion || noVersions) {
611cb0ef41Sopenharmony_ci      await npmFetch(`${pkgUri}/-rev/${pkg._rev}`, {
621cb0ef41Sopenharmony_ci        ...opts,
631cb0ef41Sopenharmony_ci        method: 'DELETE',
641cb0ef41Sopenharmony_ci        ignoreBody: true,
651cb0ef41Sopenharmony_ci      })
661cb0ef41Sopenharmony_ci      return true
671cb0ef41Sopenharmony_ci    } else {
681cb0ef41Sopenharmony_ci      const dist = allVersions[version].dist
691cb0ef41Sopenharmony_ci      delete allVersions[version]
701cb0ef41Sopenharmony_ci
711cb0ef41Sopenharmony_ci      const latestVer = pkg['dist-tags'].latest
721cb0ef41Sopenharmony_ci
731cb0ef41Sopenharmony_ci      // deleting dist tags associated to version
741cb0ef41Sopenharmony_ci      Object.keys(pkg['dist-tags']).forEach(tag => {
751cb0ef41Sopenharmony_ci        if (pkg['dist-tags'][tag] === version) {
761cb0ef41Sopenharmony_ci          delete pkg['dist-tags'][tag]
771cb0ef41Sopenharmony_ci        }
781cb0ef41Sopenharmony_ci      })
791cb0ef41Sopenharmony_ci
801cb0ef41Sopenharmony_ci      if (latestVer === version) {
811cb0ef41Sopenharmony_ci        pkg['dist-tags'].latest = Object.keys(
821cb0ef41Sopenharmony_ci          allVersions
831cb0ef41Sopenharmony_ci        ).sort(semver.compareLoose).pop()
841cb0ef41Sopenharmony_ci      }
851cb0ef41Sopenharmony_ci
861cb0ef41Sopenharmony_ci      delete pkg._revisions
871cb0ef41Sopenharmony_ci      delete pkg._attachments
881cb0ef41Sopenharmony_ci
891cb0ef41Sopenharmony_ci      // Update packument with removed versions
901cb0ef41Sopenharmony_ci      await npmFetch(`${pkgUri}/-rev/${pkg._rev}`, {
911cb0ef41Sopenharmony_ci        ...opts,
921cb0ef41Sopenharmony_ci        method: 'PUT',
931cb0ef41Sopenharmony_ci        body: pkg,
941cb0ef41Sopenharmony_ci        ignoreBody: true,
951cb0ef41Sopenharmony_ci      })
961cb0ef41Sopenharmony_ci
971cb0ef41Sopenharmony_ci      // Remove the tarball itself
981cb0ef41Sopenharmony_ci      const { _rev } = await npmFetch.json(pkgUri, {
991cb0ef41Sopenharmony_ci        ...opts,
1001cb0ef41Sopenharmony_ci        query: { write: true },
1011cb0ef41Sopenharmony_ci      })
1021cb0ef41Sopenharmony_ci      const tarballUrl = getPathname(dist.tarball, opts.registry)
1031cb0ef41Sopenharmony_ci      await npmFetch(`${tarballUrl}/-rev/${_rev}`, {
1041cb0ef41Sopenharmony_ci        ...opts,
1051cb0ef41Sopenharmony_ci        method: 'DELETE',
1061cb0ef41Sopenharmony_ci        ignoreBody: true,
1071cb0ef41Sopenharmony_ci      })
1081cb0ef41Sopenharmony_ci      return true
1091cb0ef41Sopenharmony_ci    }
1101cb0ef41Sopenharmony_ci  } catch (err) {
1111cb0ef41Sopenharmony_ci    if (err.code !== 'E404') {
1121cb0ef41Sopenharmony_ci      throw err
1131cb0ef41Sopenharmony_ci    }
1141cb0ef41Sopenharmony_ci
1151cb0ef41Sopenharmony_ci    return true
1161cb0ef41Sopenharmony_ci  }
1171cb0ef41Sopenharmony_ci}
1181cb0ef41Sopenharmony_ci
1191cb0ef41Sopenharmony_cimodule.exports = unpublish
120