• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict'
2
3const figgyPudding = require('figgy-pudding')
4const npa = require('npm-package-arg')
5const npmFetch = require('npm-registry-fetch')
6const semver = require('semver')
7const url = require('url')
8
9const UnpublishConfig = figgyPudding({
10  force: { default: false },
11  Promise: { default: () => Promise }
12})
13
14module.exports = unpublish
15function unpublish (spec, opts) {
16  opts = UnpublishConfig(opts)
17  return new opts.Promise(resolve => resolve()).then(() => {
18    spec = npa(spec)
19    // NOTE: spec is used to pick the appropriate registry/auth combo.
20    opts = opts.concat({ spec })
21    const pkgUri = spec.escapedName
22    return npmFetch.json(pkgUri, opts.concat({
23      query: { write: true }
24    })).then(pkg => {
25      if (!spec.rawSpec || spec.rawSpec === '*') {
26        return npmFetch(`${pkgUri}/-rev/${pkg._rev}`, opts.concat({
27          method: 'DELETE',
28          ignoreBody: true
29        }))
30      } else {
31        const version = spec.rawSpec
32        const allVersions = pkg.versions || {}
33        const versionPublic = allVersions[version]
34        let dist
35        if (versionPublic) {
36          dist = allVersions[version].dist
37        }
38        delete allVersions[version]
39        // if it was the only version, then delete the whole package.
40        if (!Object.keys(allVersions).length) {
41          return npmFetch(`${pkgUri}/-rev/${pkg._rev}`, opts.concat({
42            method: 'DELETE',
43            ignoreBody: true
44          }))
45        } else if (versionPublic) {
46          const latestVer = pkg['dist-tags'].latest
47          Object.keys(pkg['dist-tags']).forEach(tag => {
48            if (pkg['dist-tags'][tag] === version) {
49              delete pkg['dist-tags'][tag]
50            }
51          })
52
53          if (latestVer === version) {
54            pkg['dist-tags'].latest = Object.keys(
55              allVersions
56            ).sort(semver.compareLoose).pop()
57          }
58
59          delete pkg._revisions
60          delete pkg._attachments
61          // Update packument with removed versions
62          return npmFetch(`${pkgUri}/-rev/${pkg._rev}`, opts.concat({
63            method: 'PUT',
64            body: pkg,
65            ignoreBody: true
66          })).then(() => {
67            // Remove the tarball itself
68            return npmFetch.json(pkgUri, opts.concat({
69              query: { write: true }
70            })).then(({ _rev, _id }) => {
71              const tarballUrl = url.parse(dist.tarball).pathname.substr(1)
72              return npmFetch(`${tarballUrl}/-rev/${_rev}`, opts.concat({
73                method: 'DELETE',
74                ignoreBody: true
75              }))
76            })
77          })
78        }
79      }
80    }, err => {
81      if (err.code !== 'E404') {
82        throw err
83      }
84    })
85  }).then(() => true)
86}
87