1'use strict' 2var fs = require('graceful-fs') 3var path = require('path') 4var chain = require('slide').chain 5var iferr = require('iferr') 6var rimraf = require('rimraf') 7var mkdirp = require('gentle-fs').mkdir 8var rmStuff = require('../../unbuild.js').rmStuff 9var lifecycle = require('../../utils/lifecycle.js') 10var move = require('../../utils/move.js') 11 12/* 13 Move a module from one point in the node_modules tree to another. 14 Do not disturb either the source or target location's node_modules 15 folders. 16*/ 17 18module.exports = function (staging, pkg, log, next) { 19 log.silly('move', pkg.fromPath, pkg.path) 20 chain([ 21 [lifecycle, pkg.package, 'preuninstall', pkg.fromPath, { failOk: true }], 22 [lifecycle, pkg.package, 'uninstall', pkg.fromPath, { failOk: true }], 23 [rmStuff, pkg.package, pkg.fromPath], 24 [lifecycle, pkg.package, 'postuninstall', pkg.fromPath, { failOk: true }], 25 [moveModuleOnly, pkg.fromPath, pkg.path, log], 26 [lifecycle, pkg.package, 'preinstall', pkg.path, { failOk: true }], 27 [removeEmptyParents, path.resolve(pkg.fromPath, '..')] 28 ], next) 29} 30 31function removeEmptyParents (pkgdir, next) { 32 fs.rmdir(pkgdir, function (er) { 33 // FIXME: Make sure windows does what we want here 34 if (er && er.code !== 'ENOENT') return next() 35 removeEmptyParents(path.resolve(pkgdir, '..'), next) 36 }) 37} 38 39function moveModuleOnly (from, to, log, done) { 40 var fromModules = path.join(from, 'node_modules') 41 var tempFromModules = from + '.node_modules' 42 var toModules = path.join(to, 'node_modules') 43 var tempToModules = to + '.node_modules' 44 45 log.silly('move', 'move existing destination node_modules away', toModules) 46 47 move(toModules, tempToModules).then(removeDestination(done), removeDestination(done)) 48 49 function removeDestination (next) { 50 return function (er) { 51 log.silly('move', 'remove existing destination', to) 52 if (er) { 53 rimraf(to, iferr(next, makeDestination(next))) 54 } else { 55 rimraf(to, iferr(next, makeDestination(iferr(next, moveToModulesBack(next))))) 56 } 57 } 58 } 59 60 function moveToModulesBack (next) { 61 return function () { 62 log.silly('move', 'move existing destination node_modules back', toModules) 63 move(tempToModules, toModules).then(next, done) 64 } 65 } 66 67 function makeDestination (next) { 68 return function () { 69 log.silly('move', 'make sure destination parent exists', path.resolve(to, '..')) 70 mkdirp(path.resolve(to, '..'), iferr(done, moveNodeModules(next))) 71 } 72 } 73 74 function moveNodeModules (next) { 75 return function () { 76 log.silly('move', 'move source node_modules away', fromModules) 77 move(fromModules, tempFromModules).then(doMove(moveNodeModulesBack(next)), doMove(next)) 78 } 79 } 80 81 function doMove (next) { 82 return function () { 83 log.silly('move', 'move module dir to final dest', from, to) 84 move(from, to).then(next, done) 85 } 86 } 87 88 function moveNodeModulesBack (next) { 89 return function () { 90 mkdirp(from, iferr(done, function () { 91 log.silly('move', 'put source node_modules back', fromModules) 92 move(tempFromModules, fromModules).then(next, done) 93 })) 94 } 95 } 96} 97