| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106 | 'use strict'const path = require('path')const fs = require('graceful-fs')const Bluebird = require('bluebird')const rimraf = Bluebird.promisify(require('rimraf'))const mkdirp = Bluebird.promisify(require('gentle-fs').mkdir)const lstat = Bluebird.promisify(fs.lstat)const readdir = Bluebird.promisify(fs.readdir)const symlink = Bluebird.promisify(fs.symlink)const gentlyRm = Bluebird.promisify(require('../../utils/gently-rm'))const moduleStagingPath = require('../module-staging-path.js')const move = require('move-concurrently')const moveOpts = {fs: fs, Promise: Bluebird, maxConcurrency: 4}const getRequested = require('../get-requested.js')const log = require('npmlog')const packageId = require('../../utils/package-id.js')module.exports = function (staging, pkg, log) {  log.silly('finalize', pkg.realpath)  const extractedTo = moduleStagingPath(staging, pkg)  const delpath = path.join(path.dirname(pkg.realpath), '.' + path.basename(pkg.realpath) + '.DELETE')  let movedDestAway = false  const requested = pkg.package._requested || getRequested(pkg)  if (requested.type === 'directory') {    const relative = path.relative(path.dirname(pkg.path), pkg.realpath)    return makeParentPath(pkg.path)      .then(() => symlink(relative, pkg.path, 'junction'))      .catch((ex) => {        return rimraf(pkg.path).then(() => symlink(relative, pkg.path, 'junction'))      })  } else {    return makeParentPath(pkg.realpath)      .then(moveStagingToDestination)      .then(restoreOldNodeModules)      .catch((err) => {        if (movedDestAway) {          return rimraf(pkg.realpath).then(moveOldDestinationBack).then(() => {            throw err          })        } else {          throw err        }      })      .then(() => rimraf(delpath))  }  function makeParentPath (dir) {    return mkdirp(path.dirname(dir))  }  function moveStagingToDestination () {    return destinationIsClear()      .then(actuallyMoveStaging)      .catch(() => moveOldDestinationAway().then(actuallyMoveStaging))  }  function destinationIsClear () {    return lstat(pkg.realpath).then(() => {      throw new Error('destination exists')    }, () => {})  }  function actuallyMoveStaging () {    return move(extractedTo, pkg.realpath, moveOpts)  }  function moveOldDestinationAway () {    return rimraf(delpath).then(() => {      return move(pkg.realpath, delpath, moveOpts)    }).then(() => { movedDestAway = true })  }  function moveOldDestinationBack () {    return move(delpath, pkg.realpath, moveOpts).then(() => { movedDestAway = false })  }  function restoreOldNodeModules () {    if (!movedDestAway) return    return readdir(path.join(delpath, 'node_modules')).catch(() => []).then((modules) => {      if (!modules.length) return      return mkdirp(path.join(pkg.realpath, 'node_modules')).then(() => Bluebird.map(modules, (file) => {        const from = path.join(delpath, 'node_modules', file)        const to = path.join(pkg.realpath, 'node_modules', file)        return move(from, to, moveOpts)      }))    })  }}module.exports.rollback = function (top, staging, pkg) {  return Bluebird.try(() => {    const requested = pkg.package._requested || getRequested(pkg)    if (requested && requested.type === 'directory') return Promise.resolve()    // strictly speaking rolling back a finalize should ONLY remove module that    // was being finalized, not any of the things under it. But currently    // those modules are guaranteed to be useless so we may as well remove them too.    // When/if we separate `commit` step and can rollback to previous versions    // of upgraded modules then we'll need to revisit this…    return gentlyRm(pkg.path, false, top).catch((err) => {      log.warn('rollback', `Rolling back ${packageId(pkg)} failed (this is probably harmless): ${err.message ? err.message : err}`)    })  })}
 |