1module.exports = unbuild 2module.exports.rmStuff = rmStuff 3unbuild.usage = 'npm unbuild <folder>\n(this is plumbing)' 4 5const readJson = require('read-package-json') 6const gentlyRm = require('./utils/gently-rm.js') 7const npm = require('./npm.js') 8const path = require('path') 9const isInside = require('path-is-inside') 10const lifecycle = require('./utils/lifecycle.js') 11const asyncMap = require('slide').asyncMap 12const chain = require('slide').chain 13const log = require('npmlog') 14const build = require('./build.js') 15const output = require('./utils/output.js') 16 17// args is a list of folders. 18// remove any bins/etc, and then delete the folder. 19function unbuild (args, silent, cb) { 20 if (typeof silent === 'function') { 21 cb = silent 22 silent = false 23 } 24 asyncMap(args, unbuild_(silent), cb) 25} 26 27function unbuild_ (silent) { 28 return function (folder, cb_) { 29 function cb (er) { 30 cb_(er, path.relative(npm.root, folder)) 31 } 32 folder = path.resolve(folder) 33 const base = isInside(folder, npm.prefix) ? npm.prefix : folder 34 delete build._didBuild[folder] 35 log.verbose('unbuild', folder.substr(npm.prefix.length + 1)) 36 readJson(path.resolve(folder, 'package.json'), function (er, pkg) { 37 // if no json, then just trash it, but no scripts or whatever. 38 if (er) return gentlyRm(folder, false, base, cb) 39 chain( 40 [ 41 [lifecycle, pkg, 'preuninstall', folder, { failOk: true }], 42 [lifecycle, pkg, 'uninstall', folder, { failOk: true }], 43 !silent && function (cb) { 44 output('unbuild ' + pkg._id) 45 cb() 46 }, 47 [rmStuff, pkg, folder], 48 [lifecycle, pkg, 'postuninstall', folder, { failOk: true }], 49 [gentlyRm, folder, false, base] 50 ], 51 cb 52 ) 53 }) 54 } 55} 56 57function rmStuff (pkg, folder, cb) { 58 // if it's global, and folder is in {prefix}/node_modules, 59 // then bins are in {prefix}/bin 60 // otherwise, then bins are in folder/../.bin 61 const dir = path.dirname(folder) 62 const scope = path.basename(dir) 63 const parent = scope.charAt(0) === '@' ? path.dirname(dir) : dir 64 const gnm = npm.dir 65 // gnm might be an absolute path, parent might be relative 66 // this checks they're the same directory regardless 67 const top = path.relative(gnm, parent) === '' 68 69 log.verbose('unbuild rmStuff', pkg._id, 'from', gnm) 70 if (!top) log.verbose('unbuild rmStuff', 'in', parent) 71 asyncMap([rmBins, rmMans], function (fn, cb) { 72 fn(pkg, folder, parent, top, cb) 73 }, cb) 74} 75 76function rmBins (pkg, folder, parent, top, cb) { 77 if (!pkg.bin) return cb() 78 const binRoot = top ? npm.bin : path.resolve(parent, '.bin') 79 asyncMap(Object.keys(pkg.bin), function (b, cb) { 80 if (process.platform === 'win32') { 81 chain([ 82 [gentlyRm, path.resolve(binRoot, b) + '.ps1', true, folder], 83 [gentlyRm, path.resolve(binRoot, b) + '.cmd', true, folder], 84 [gentlyRm, path.resolve(binRoot, b), true, folder] 85 ], cb) 86 } else { 87 gentlyRm(path.resolve(binRoot, b), true, folder, cb) 88 } 89 }, gentlyRmBinRoot) 90 91 function gentlyRmBinRoot (err) { 92 if (err || top) return cb(err) 93 return gentlyRm(binRoot, true, parent, cb) 94 } 95} 96 97function rmMans (pkg, folder, parent, top, cb) { 98 if (!pkg.man || 99 !top || 100 process.platform === 'win32' || 101 !npm.config.get('global')) { 102 return cb() 103 } 104 const manRoot = path.resolve(npm.config.get('prefix'), 'share', 'man') 105 log.verbose('rmMans', 'man files are', pkg.man, 'in', manRoot) 106 asyncMap(pkg.man, function (man, cb) { 107 if (Array.isArray(man)) { 108 man.forEach(rmMan) 109 } else { 110 rmMan(man) 111 } 112 113 function rmMan (man) { 114 log.silly('rmMan', 'preparing to remove', man) 115 const parseMan = man.match(/(.*\.([0-9]+)(\.gz)?)$/) 116 if (!parseMan) { 117 log.error( 118 'rmMan', man, 'is not a valid name for a man file.', 119 'Man files must end with a number, ' + 120 'and optionally a .gz suffix if they are compressed.' 121 ) 122 return cb() 123 } 124 125 const stem = parseMan[1] 126 const sxn = parseMan[2] 127 const gz = parseMan[3] || '' 128 const bn = path.basename(stem) 129 const manDest = path.join( 130 manRoot, 131 'man' + sxn, 132 (bn.indexOf(pkg.name) === 0 ? bn : pkg.name + '-' + bn) + '.' + sxn + gz 133 ) 134 gentlyRm(manDest, true, cb) 135 } 136 }, cb) 137} 138