You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

172 lines
5.7 KiB

'use strict'
var npm = require('../npm.js')
var validate = require('aproba')
var npa = require('npm-package-arg')
var flattenTree = require('./flatten-tree.js')
var isOnlyDev = require('./is-only-dev.js')
var log = require('npmlog')
var path = require('path')
function nonRegistrySource (pkg) {
validate('O', arguments)
var requested = pkg._requested || (pkg._from && npa(pkg._from))
if (!requested) return false
if (requested.type === 'hosted') return true
if (requested.type === 'file' || requested.type === 'directory') return true
return false
}
function pkgAreEquiv (aa, bb) {
var aaSha = (aa.dist && aa.dist.integrity) || aa._integrity
var bbSha = (bb.dist && bb.dist.integrity) || bb._integrity
if (aaSha === bbSha) return true
if (aaSha || bbSha) return false
if (nonRegistrySource(aa) || nonRegistrySource(bb)) return false
if (aa.version === bb.version) return true
return false
}
function getUniqueId (pkg) {
var versionspec = pkg._integrity
if (!versionspec && nonRegistrySource(pkg)) {
if (pkg._requested) {
versionspec = pkg._requested.fetchSpec
} else if (pkg._from) {
versionspec = npa(pkg._from).fetchSpec
}
}
if (!versionspec) {
versionspec = pkg.version
}
return pkg.name + '@' + versionspec
}
function pushAll (aa, bb) {
Array.prototype.push.apply(aa, bb)
}
module.exports = function (oldTree, newTree, differences, log, next) {
validate('OOAOF', arguments)
pushAll(differences, sortActions(diffTrees(oldTree, newTree)))
log.finish()
next()
}
function isNotTopOrExtraneous (node) {
return !node.isTop && !node.userRequired && !node.existing
}
var sortActions = module.exports.sortActions = function (differences) {
var actions = {}
differences.forEach(function (action) {
var child = action[1]
actions[child.location] = action
})
var sorted = []
var added = {}
var sortedlocs = Object.keys(actions).sort(sortByLocation)
// We're going to sort the actions taken on top level dependencies first, before
// considering the order of transitive deps. Because we're building our list
// from the bottom up, this means we will return a list with top level deps LAST.
// This is important in terms of keeping installations as consistent as possible
// as folks add new dependencies.
var toplocs = sortedlocs.filter(function (location) {
var mod = actions[location][1]
if (!mod.requiredBy) return true
// If this module is required by any non-top level module
// or by any extraneous module, eg user requested or existing
// then we don't want to give this priority sorting.
return !mod.requiredBy.some(isNotTopOrExtraneous)
})
toplocs.concat(sortedlocs).forEach(function (location) {
sortByDeps(actions[location])
})
function sortByLocation (aa, bb) {
return bb.localeCompare(aa)
}
function sortModuleByLocation (aa, bb) {
return sortByLocation(aa && aa.location, bb && bb.location)
}
function sortByDeps (action) {
var mod = action[1]
if (added[mod.location]) return
added[mod.location] = action
if (!mod.requiredBy) mod.requiredBy = []
mod.requiredBy.sort(sortModuleByLocation).forEach(function (mod) {
if (actions[mod.location]) sortByDeps(actions[mod.location])
})
sorted.unshift(action)
}
return sorted
}
function setAction (differences, action, pkg) {
differences.push([action, pkg])
}
var diffTrees = module.exports._diffTrees = function (oldTree, newTree) {
validate('OO', arguments)
var differences = []
var flatOldTree = flattenTree(oldTree)
var flatNewTree = flattenTree(newTree)
var toRemove = {}
var toRemoveByUniqueId = {}
// find differences
Object.keys(flatOldTree).forEach(function (flatname) {
if (flatNewTree[flatname]) return
var pkg = flatOldTree[flatname]
if (pkg.isInLink && /^[.][.][/\\]/.test(path.relative(newTree.realpath, pkg.realpath))) return
toRemove[flatname] = pkg
var pkgunique = getUniqueId(pkg.package)
if (!toRemoveByUniqueId[pkgunique]) toRemoveByUniqueId[pkgunique] = []
toRemoveByUniqueId[pkgunique].push(flatname)
})
Object.keys(flatNewTree).forEach(function (path) {
var pkg = flatNewTree[path]
pkg.oldPkg = flatOldTree[path]
if (pkg.oldPkg) {
if (!pkg.userRequired && pkgAreEquiv(pkg.oldPkg.package, pkg.package)) return
setAction(differences, 'update', pkg)
} else {
var vername = getUniqueId(pkg.package)
var removing = toRemoveByUniqueId[vername] && toRemoveByUniqueId[vername].length
var bundlesOrFromBundle = pkg.fromBundle || pkg.package.bundleDependencies
if (removing && !bundlesOrFromBundle) {
var flatname = toRemoveByUniqueId[vername].shift()
pkg.fromPath = toRemove[flatname].path
setAction(differences, 'move', pkg)
delete toRemove[flatname]
} else if (!(pkg.isInLink && pkg.fromBundle)) {
setAction(differences, 'add', pkg)
}
}
})
Object
.keys(toRemove)
.map((path) => toRemove[path])
.forEach((pkg) => setAction(differences, 'remove', pkg))
const includeDev = npm.config.get('dev') ||
(!/^prod(uction)?$/.test(npm.config.get('only')) && !npm.config.get('production')) ||
/^dev(elopment)?$/.test(npm.config.get('only')) ||
/^dev(elopment)?$/.test(npm.config.get('also'))
const includeProd = !/^dev(elopment)?$/.test(npm.config.get('only'))
if (!includeProd || !includeDev) {
log.silly('diff-trees', 'filtering actions:', 'includeDev', includeDev, 'includeProd', includeProd)
differences = differences.filter((diff) => {
const pkg = diff[1]
const pkgIsOnlyDev = isOnlyDev(pkg)
return (!includeProd && pkgIsOnlyDev) || (includeDev && pkgIsOnlyDev) || (includeProd && !pkgIsOnlyDev)
})
}
return differences
}