1 | 'use strict'
|
2 |
|
3 | const { URL } = require('url')
|
4 | const npa = require('npm-package-arg')
|
5 | const npmFetch = require('npm-registry-fetch')
|
6 | const semver = require('semver')
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 | const getPathname = (tarball, registry) => {
|
13 | const registryUrl = new URL(registry).pathname.slice(1)
|
14 | let tarballUrl = new URL(tarball).pathname.slice(1)
|
15 |
|
16 |
|
17 |
|
18 |
|
19 | if (registryUrl) {
|
20 | tarballUrl = tarballUrl.slice(registryUrl.length)
|
21 | }
|
22 | return tarballUrl
|
23 | }
|
24 |
|
25 | const unpublish = async (spec, opts) => {
|
26 | spec = npa(spec)
|
27 |
|
28 | opts = {
|
29 | force: false,
|
30 | ...opts,
|
31 | spec,
|
32 | }
|
33 |
|
34 | try {
|
35 | const pkgUri = spec.escapedName
|
36 | const pkg = await npmFetch.json(pkgUri, {
|
37 | ...opts,
|
38 | query: { write: true },
|
39 | })
|
40 |
|
41 | const version = spec.rawSpec
|
42 | const allVersions = pkg.versions || {}
|
43 | const versionData = allVersions[version]
|
44 |
|
45 | const rawSpecs = (!spec.rawSpec || spec.rawSpec === '*')
|
46 | const onlyVersion = Object.keys(allVersions).length === 1
|
47 | const noVersions = !Object.keys(allVersions).length
|
48 |
|
49 |
|
50 |
|
51 | if (!versionData && !rawSpecs && !noVersions) {
|
52 | return true
|
53 | }
|
54 |
|
55 |
|
56 |
|
57 |
|
58 |
|
59 |
|
60 | if (rawSpecs || onlyVersion || noVersions) {
|
61 | await npmFetch(`${pkgUri}/-rev/${pkg._rev}`, {
|
62 | ...opts,
|
63 | method: 'DELETE',
|
64 | ignoreBody: true,
|
65 | })
|
66 | return true
|
67 | } else {
|
68 | const dist = allVersions[version].dist
|
69 | delete allVersions[version]
|
70 |
|
71 | const latestVer = pkg['dist-tags'].latest
|
72 |
|
73 |
|
74 | Object.keys(pkg['dist-tags']).forEach(tag => {
|
75 | if (pkg['dist-tags'][tag] === version) {
|
76 | delete pkg['dist-tags'][tag]
|
77 | }
|
78 | })
|
79 |
|
80 | if (latestVer === version) {
|
81 | pkg['dist-tags'].latest = Object.keys(
|
82 | allVersions
|
83 | ).sort(semver.compareLoose).pop()
|
84 | }
|
85 |
|
86 | delete pkg._revisions
|
87 | delete pkg._attachments
|
88 |
|
89 |
|
90 | await npmFetch(`${pkgUri}/-rev/${pkg._rev}`, {
|
91 | ...opts,
|
92 | method: 'PUT',
|
93 | body: pkg,
|
94 | ignoreBody: true,
|
95 | })
|
96 |
|
97 |
|
98 | const { _rev } = await npmFetch.json(pkgUri, {
|
99 | ...opts,
|
100 | query: { write: true },
|
101 | })
|
102 | const tarballUrl = getPathname(dist.tarball, opts.registry)
|
103 | await npmFetch(`${tarballUrl}/-rev/${_rev}`, {
|
104 | ...opts,
|
105 | method: 'DELETE',
|
106 | ignoreBody: true,
|
107 | })
|
108 | return true
|
109 | }
|
110 | } catch (err) {
|
111 | if (err.code !== 'E404') {
|
112 | throw err
|
113 | }
|
114 |
|
115 | return true
|
116 | }
|
117 | }
|
118 |
|
119 | module.exports = unpublish
|