unbuild.js 4.3 KB

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