| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160 | var util = require('util')var path = require('path')var validate = require('aproba')var without = require('lodash.without')var asyncMap = require('slide').asyncMapvar chain = require('slide').chainvar npa = require('npm-package-arg')var log = require('npmlog')var npm = require('./npm.js')var Installer = require('./install.js').Installervar findRequirement = require('./install/deps.js').findRequirementvar earliestInstallable = require('./install/deps.js').earliestInstallablevar checkPermissions = require('./install/check-permissions.js')var decomposeActions = require('./install/decompose-actions.js')var loadExtraneous = require('./install/deps.js').loadExtraneousvar computeMetadata = require('./install/deps.js').computeMetadatavar sortActions = require('./install/diff-trees.js').sortActionsvar moduleName = require('./utils/module-name.js')var packageId = require('./utils/package-id.js')var childPath = require('./utils/child-path.js')var usage = require('./utils/usage')var getRequested = require('./install/get-requested.js')module.exports = dedupemodule.exports.Deduper = Deduperdedupe.usage = usage(  'dedupe',  'npm dedupe')function dedupe (args, cb) {  validate('AF', arguments)  // the /path/to/node_modules/..  var where = path.resolve(npm.dir, '..')  var dryrun = false  if (npm.command.match(/^find/)) dryrun = true  if (npm.config.get('dry-run')) dryrun = true  if (dryrun && !npm.config.get('json')) npm.config.set('parseable', true)  new Deduper(where, dryrun).run(cb)}function Deduper (where, dryrun) {  validate('SB', arguments)  Installer.call(this, where, dryrun, [])  this.noPackageJsonOk = true  this.topLevelLifecycles = false}util.inherits(Deduper, Installer)Deduper.prototype.loadIdealTree = function (cb) {  validate('F', arguments)  log.silly('install', 'loadIdealTree')  var self = this  chain([    [this.newTracker(this.progress.loadIdealTree, 'cloneCurrentTree')],    [this, this.cloneCurrentTreeToIdealTree],    [this, this.finishTracker, 'cloneCurrentTree'],    [this.newTracker(this.progress.loadIdealTree, 'loadAllDepsIntoIdealTree', 10)],    [ function (next) {      loadExtraneous(self.idealTree, self.progress.loadAllDepsIntoIdealTree, next)    } ],    [this, this.finishTracker, 'loadAllDepsIntoIdealTree'],    [this, andComputeMetadata(this.idealTree)]  ], cb)}function andComputeMetadata (tree) {  return function (next) {    next(null, computeMetadata(tree))  }}Deduper.prototype.generateActionsToTake = function (cb) {  validate('F', arguments)  log.silly('dedupe', 'generateActionsToTake')  chain([    [this.newTracker(log, 'hoist', 1)],    [hoistChildren, this.idealTree, this.differences],    [this, this.finishTracker, 'hoist'],    [this.newTracker(log, 'sort-actions', 1)],    [this, function (next) {      this.differences = sortActions(this.differences)      next()    }],    [this, this.finishTracker, 'sort-actions'],    [checkPermissions, this.differences],    [decomposeActions, this.differences, this.todo]  ], cb)}function move (node, hoistTo, diff) {  node.parent.children = without(node.parent.children, node)  hoistTo.children.push(node)  node.fromPath = node.path  node.path = childPath(hoistTo.path, node)  node.parent = hoistTo  if (!diff.filter(function (action) { return action[0] === 'move' && action[1] === node }).length) {    diff.push(['move', node])  }}function moveRemainingChildren (node, diff) {  node.children.forEach(function (child) {    move(child, node, diff)    moveRemainingChildren(child, diff)  })}function remove (child, diff, done) {  remove_(child, diff, new Set(), done)}function remove_ (child, diff, seen, done) {  if (seen.has(child)) return done()  seen.add(child)  diff.push(['remove', child])  child.parent.children = without(child.parent.children, child)  asyncMap(child.children, function (child, next) {    remove_(child, diff, seen, next)  }, done)}function hoistChildren (tree, diff, next) {  hoistChildren_(tree, diff, new Set(), next)}function hoistChildren_ (tree, diff, seen, next) {  validate('OAOF', arguments)  if (seen.has(tree)) return next()  seen.add(tree)  asyncMap(tree.children, function (child, done) {    if (!tree.parent || child.fromBundle || child.package._inBundle) return hoistChildren_(child, diff, seen, done)    var better = findRequirement(tree.parent, moduleName(child), getRequested(child) || npa(packageId(child)))    if (better) {      return chain([        [remove, child, diff],        [andComputeMetadata(tree)]      ], done)    }    var hoistTo = earliestInstallable(tree, tree.parent, child.package, log)    if (hoistTo) {      move(child, hoistTo, diff)      chain([        [andComputeMetadata(hoistTo)],        [hoistChildren_, child, diff, seen],        [ function (next) {          moveRemainingChildren(child, diff)          next()        } ]      ], done)    } else {      done()    }  }, next)}
 |