Files
bower/lib/util/prune.js
2013-02-12 19:07:03 +00:00

103 lines
3.3 KiB
JavaScript

// ==========================================
// BOWER: prune
// ==========================================
// Copyright 2012 Twitter, Inc
// Licensed under The MIT License
// http://opensource.org/licenses/MIT
// ==========================================
var semver = require('semver');
var sort = require('stable');
var versionRequirements = function (dependencyMap) {
var result = {};
for (var name in dependencyMap) {
dependencyMap[name].forEach(function (pkg) {
result[name] = result[name] || [];
if (pkg.originalTag && result[name].indexOf(pkg.originalTag) === -1) {
result[name].push(pkg.originalTag);
}
});
}
return result;
};
var validVersions = function (versions, dependency) {
if (!versions || !versions.length) return true;
// If a non resolved dependency is passed, we simply ignore it
if (!dependency.version) return false;
if (!semver.valid(dependency.version)) {
throw new Error('Invalid semver version ' + dependency.version + ' specified in ' + dependency.name);
}
return versions.every(function (version) {
return semver.satisfies(dependency.version, version);
});
};
module.exports = function (dependencyMap, forceLatest) {
// generate version requirements
// compare dependency map with version requirements
// check for conflicts
// select best version
// return an object with the resolved deps, conflict deps and forcebly resolved ones
var resolved = {};
var conflicted = null;
var forceblyResolved = null;
var versionMap = versionRequirements(dependencyMap);
var sortFunc = function (a, b) {
if (semver.gt(a.version, b.version)) return -1;
if (semver.lt(a.version, b.version)) return 1;
// If the comparison determines that both packages are equal, do not give priority to local ones
if (a.path === a.localPath && b.path !== b.localPath) return 1;
return 0;
};
// Note that bellow we use a stable sort algorithm
// This is because if two packages are equal, the initial order should be respected
for (var name in dependencyMap) {
var matches = dependencyMap[name].filter(validVersions.bind(this, versionMap[name]));
if (!matches.length) {
// No resolvable dependency
// We resolve to the latest package if the forceLatest is true
// Otherwise, check if any of those are root packages
// If so, we assume that as the resolver (with a warning)
// Otherwise there's a conflict
if (forceLatest) {
forceblyResolved = forceblyResolved || {};
forceblyResolved[name] = sort(dependencyMap[name], sortFunc);
continue;
}
matches = dependencyMap[name].filter(function (pkg) { return !!pkg.root; });
if (matches.length) {
forceblyResolved = forceblyResolved || {};
forceblyResolved[name] = dependencyMap[name].sort(function (a, b) {
if (a.root && b.root) return sortFunc(a, b);
if (a.root) return -1;
if (b.root) return 1;
return sortFunc(a, b);
});
} else {
conflicted = conflicted || {};
conflicted[name] = dependencyMap[name];
}
} else {
resolved[name] = [ sort(matches, sortFunc)[0] ];
}
}
return {
resolved: resolved,
conflicted: conflicted,
forceblyResolved: forceblyResolved
};
};