1'use strict' 2var path = require('path') 3var fs = require('graceful-fs') 4var rimraf = require('rimraf') 5var asyncMap = require('slide').asyncMap 6var mkdirp = require('gentle-fs').mkdir 7var npm = require('../../npm.js') 8var andIgnoreErrors = require('../and-ignore-errors.js') 9var move = require('../../utils/move.js') 10var isInside = require('path-is-inside') 11var vacuum = require('fs-vacuum') 12 13// This is weird because we want to remove the module but not it's node_modules folder 14// allowing for this allows us to not worry about the order of operations 15module.exports = function (staging, pkg, log, next) { 16 log.silly('remove', pkg.path) 17 if (pkg.target) { 18 removeLink(pkg, next) 19 } else { 20 removeDir(pkg, log, next) 21 } 22} 23 24function removeLink (pkg, next) { 25 var base = isInside(pkg.path, npm.prefix) ? npm.prefix : pkg.path 26 rimraf(pkg.path, (err) => { 27 if (err) return next(err) 28 vacuum(pkg.path, {base: base}, next) 29 }) 30} 31 32function removeDir (pkg, log, next) { 33 var modpath = path.join(path.dirname(pkg.path), '.' + path.basename(pkg.path) + '.MODULES') 34 35 move(path.join(pkg.path, 'node_modules'), modpath).then(unbuildPackage, unbuildPackage) 36 37 function unbuildPackage (moveEr) { 38 rimraf(pkg.path, moveEr ? andRemoveEmptyParents(pkg.path) : moveModulesBack) 39 } 40 41 function andRemoveEmptyParents (path) { 42 return function (er) { 43 if (er) return next(er) 44 removeEmptyParents(pkg.path) 45 } 46 } 47 48 function moveModulesBack () { 49 fs.readdir(modpath, makeTarget) 50 } 51 52 function makeTarget (readdirEr, files) { 53 if (readdirEr) return cleanup() 54 if (!files.length) return cleanup() 55 mkdirp(path.join(pkg.path, 'node_modules'), function (mkdirEr) { moveModules(mkdirEr, files) }) 56 } 57 58 function moveModules (mkdirEr, files) { 59 if (mkdirEr) return next(mkdirEr) 60 asyncMap(files, function (file, done) { 61 var from = path.join(modpath, file) 62 var to = path.join(pkg.path, 'node_modules', file) 63 // we ignore errors here, because they can legitimately happen, for instance, 64 // bundled modules will be in both node_modules folders 65 move(from, to).then(andIgnoreErrors(done), andIgnoreErrors(done)) 66 }, cleanup) 67 } 68 69 function cleanup () { 70 rimraf(modpath, afterCleanup) 71 } 72 73 function afterCleanup (rimrafEr) { 74 if (rimrafEr) log.warn('remove', rimrafEr) 75 removeEmptyParents(path.resolve(pkg.path, '..')) 76 } 77 78 function removeEmptyParents (pkgdir) { 79 fs.rmdir(pkgdir, function (er) { 80 // FIXME: Make sure windows does what we want here 81 if (er && er.code !== 'ENOENT') return next() 82 removeEmptyParents(path.resolve(pkgdir, '..')) 83 }) 84 } 85} 86