1const fetch = require('npm-registry-fetch') 2const otplease = require('../utils/otplease.js') 3const npa = require('npm-package-arg') 4const semver = require('semver') 5const getIdentity = require('../utils/get-identity.js') 6const libaccess = require('libnpmaccess') 7const BaseCommand = require('../base-command.js') 8 9class Deprecate extends BaseCommand { 10 static description = 'Deprecate a version of a package' 11 static name = 'deprecate' 12 static usage = ['<package-spec> <message>'] 13 static params = [ 14 'registry', 15 'otp', 16 ] 17 18 static ignoreImplicitWorkspace = false 19 20 static async completion (opts, npm) { 21 if (opts.conf.argv.remain.length > 1) { 22 return [] 23 } 24 25 const username = await getIdentity(npm, npm.flatOptions) 26 const packages = await libaccess.getPackages(username, npm.flatOptions) 27 return Object.keys(packages) 28 .filter((name) => 29 packages[name] === 'write' && 30 (opts.conf.argv.remain.length === 0 || 31 name.startsWith(opts.conf.argv.remain[0]))) 32 } 33 34 async exec ([pkg, msg]) { 35 // msg == null because '' is a valid value, it indicates undeprecate 36 if (!pkg || msg == null) { 37 throw this.usageError() 38 } 39 40 // fetch the data and make sure it exists. 41 const p = npa(pkg) 42 const spec = p.rawSpec === '*' ? '*' : p.fetchSpec 43 44 if (semver.validRange(spec, true) === null) { 45 throw new Error(`invalid version range: ${spec}`) 46 } 47 48 const uri = '/' + p.escapedName 49 const packument = await fetch.json(uri, { 50 ...this.npm.flatOptions, 51 spec: p, 52 query: { write: true }, 53 }) 54 55 const versions = Object.keys(packument.versions) 56 .filter(v => semver.satisfies(v, spec, { includePrerelease: true })) 57 58 if (versions.length) { 59 for (const v of versions) { 60 packument.versions[v].deprecated = msg 61 } 62 return otplease(this.npm, this.npm.flatOptions, opts => fetch(uri, { 63 ...opts, 64 spec: p, 65 method: 'PUT', 66 body: packument, 67 ignoreBody: true, 68 })) 69 } 70 } 71} 72 73module.exports = Deprecate 74