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