mirror of
https://github.com/bower/bower.git
synced 2026-01-23 13:17:55 -05:00
212 lines
5.8 KiB
JavaScript
212 lines
5.8 KiB
JavaScript
var crypto = require('crypto');
|
|
var fs = require('fs');
|
|
var path = require('path');
|
|
var semver = require('semver');
|
|
var mout = require('mout');
|
|
var Q = require('q');
|
|
var mkdirp = require('mkdirp');
|
|
var rimraf = require('rimraf');
|
|
|
|
function ResolveCache(dir) {
|
|
// TODO: Make some options, such as:
|
|
// - Max MB
|
|
// - Max versions per source
|
|
// - Max MB per source
|
|
// - etc..
|
|
this._dir = dir;
|
|
this._versions = {};
|
|
|
|
mkdirp.sync(dir);
|
|
}
|
|
|
|
// -----------------
|
|
|
|
ResolveCache.prototype.retrieve = function (source, target) {
|
|
var sourceId = this._getSourceId(source);
|
|
var dir = path.join(this._dir, sourceId);
|
|
|
|
target = target || '*';
|
|
|
|
return this._getVersions(source)
|
|
.then(function (versions) {
|
|
var suitable;
|
|
|
|
// If target is a semver, find a suitable version
|
|
if (semver.valid(target) != null || semver.validRange(target) != null) {
|
|
suitable = mout.array.find(versions, function (version) {
|
|
return semver.satisfies(version, target);
|
|
});
|
|
|
|
if (suitable) {
|
|
return suitable;
|
|
}
|
|
}
|
|
|
|
// If target is '*' check if there's a cached '_unversioned'
|
|
if (target === '*') {
|
|
return mout.array.find(versions, function (version) {
|
|
return version === '_unversioned';
|
|
});
|
|
}
|
|
|
|
// Otherwise check if there's an exact match
|
|
return mout.array.find(versions, function (version) {
|
|
return version === target;
|
|
});
|
|
})
|
|
.then(function (version) {
|
|
var canonicalPkg;
|
|
|
|
if (!version) {
|
|
return [];
|
|
}
|
|
|
|
// Resolve with canonical package and package meta
|
|
canonicalPkg = path.join(dir, version);
|
|
return this._readPkgMeta(canonicalPkg)
|
|
.then(function (pkgMeta) {
|
|
return [canonicalPkg, pkgMeta];
|
|
});
|
|
}.bind(this));
|
|
};
|
|
|
|
ResolveCache.prototype.store = function (canonicalPkg, pkgMeta) {
|
|
var promise = pkgMeta ? Q.resolve(pkgMeta) : this._readPkgMeta(canonicalPkg);
|
|
var sourceId;
|
|
var pkgVersion;
|
|
var dir;
|
|
|
|
return promise
|
|
.then(function (pkgMeta) {
|
|
sourceId = this._getSourceId(pkgMeta._source);
|
|
pkgVersion = pkgMeta.version || '_unversioned';
|
|
dir = path.join(this._dir, sourceId, pkgVersion);
|
|
|
|
// Check if directory exists
|
|
return Q.nfcall(fs.stat, dir)
|
|
.then(function () {
|
|
// If it does exists, remove it
|
|
return Q.nfcall(rimraf, dir);
|
|
}, function (err) {
|
|
// If directory does not exists, ensure its basename
|
|
// is created
|
|
if (err.code === 'ENOENT') {
|
|
return Q.nfcall(mkdirp, path.dirname(dir));
|
|
}
|
|
|
|
throw err;
|
|
})
|
|
// Move the canonical to sourceId/target
|
|
.then(function () {
|
|
return Q.nfcall(fs.rename, canonicalPkg, dir);
|
|
});
|
|
}.bind(this))
|
|
.then(function () {
|
|
var pkgVersion = pkgMeta.version || '_unversioned';
|
|
var versions = this._versions[sourceId];
|
|
var inCache;
|
|
|
|
if (versions) {
|
|
// Check if this exact version already exists in the
|
|
// memory cache
|
|
inCache = versions.some(function (version) {
|
|
return pkgVersion === version;
|
|
});
|
|
|
|
// If it doesn't, add it to the in memory cache
|
|
// and sort the versions afterwards
|
|
if (!inCache) {
|
|
versions.push(pkgVersion);
|
|
this._sortVersions(versions);
|
|
}
|
|
}
|
|
|
|
// Resolve with the final location
|
|
return dir;
|
|
}.bind(this));
|
|
};
|
|
|
|
ResolveCache.prototype.eliminate = function (source, version) {
|
|
// TODO:
|
|
};
|
|
|
|
ResolveCache.prototype.empty = function (source) {
|
|
// TODO:
|
|
};
|
|
|
|
// ------------------------
|
|
|
|
ResolveCache.prototype._getSourceId = function (source) {
|
|
return crypto.createHash('md5').update(source).digest('hex');
|
|
};
|
|
|
|
|
|
ResolveCache.prototype._readPkgMeta = function (dir) {
|
|
return Q.nfcall(fs.readFile, path.join(dir, '.bower.json'))
|
|
.then(function (contents) {
|
|
return JSON.parse(contents.toString());
|
|
});
|
|
};
|
|
|
|
ResolveCache.prototype._getVersions = function (source) {
|
|
var dir;
|
|
var sourceId = this._getSourceId(source);
|
|
var cache = this._versions[sourceId];
|
|
|
|
if (cache) {
|
|
return Q.resolve(cache);
|
|
}
|
|
|
|
dir = path.join(this._dir, sourceId);
|
|
return Q.nfcall(fs.readdir, dir)
|
|
.then(function (versions) {
|
|
// If there are no versions there, do not cache in memory
|
|
if (!versions.length) {
|
|
return versions;
|
|
}
|
|
|
|
// Sort and cache in memory
|
|
this._sortVersions(versions);
|
|
return this._versions[sourceId] = versions;
|
|
}.bind(this), function (err) {
|
|
// If the directory does not exists, resolve
|
|
// as an empty array
|
|
if (err.code === 'ENOENT') {
|
|
return this._versions[sourceId] = [];
|
|
}
|
|
|
|
throw err;
|
|
}.bind(this));
|
|
};
|
|
|
|
ResolveCache.prototype._sortVersions = function (versions) {
|
|
versions.sort(function (version1, version2) {
|
|
var validSemver1 = semver.valid(version1) != null;
|
|
var validSemver2 = semver.valid(version2) != null;
|
|
|
|
// If both are semvers, compare them
|
|
if (validSemver1 && validSemver2) {
|
|
if (semver.gt(version1, version2)) {
|
|
return -1;
|
|
}
|
|
if (semver.lt(version1, version2)) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// If one of them are semvers, give higher priority
|
|
if (validSemver1) {
|
|
return -1;
|
|
}
|
|
if (validSemver2) {
|
|
return 1;
|
|
}
|
|
|
|
// Otherwise they are considered equal
|
|
return 0;
|
|
});
|
|
};
|
|
|
|
module.exports = ResolveCache;
|