cdnjs
Version:
Search and URL retrieval from cdnjs
181 lines (161 loc) • 4.97 kB
JavaScript
'use strict';
var request = require('request');
var moment = require('moment');
var transform = require('cdnjs-transform');
// Search methods (return array)
var searchBy = function (key, term, array) {
var matches = [];
array.some(function (item) {
if (item[key]) {
if ((''+item[key]).toLowerCase().indexOf(term) !== -1) {
matches.push(item);
}
}
});
return matches;
};
var searchByName = searchBy.bind(null, 'name');
// Also possible:
// var searchByFilename = searchBy.bind(null, 'filename');
// var searchByDescription = searchBy.bind(null, 'description');
// Find methods (returns object or false)
var findBy = function (key, term, array) {
var match;
array.some(function (item) {
return item[key] && item[key] === term && (match = item) && true;
});
return match;
};
var findByName = findBy.bind(null, 'name');
// Also possible:
// var findByFilename = findBy.bind(null, 'filename');
// var findByDescription = findBy.bind(null, 'description');
var toggleExtension = function (name) {
var endsWithJS = /\.js$/;
if (endsWithJS.test(name)) {
name = name.replace(endsWithJS, '');
}
else {
name += '.js';
}
return name;
};
// The main cdnjs object
var cdnjs = {
urls: {
packages: 'https://cdnjs.com/packages.json',
base: '//cdnjs.cloudflare.com/ajax/libs/'
},
/**
* Build a cdnjs URL for the given package
*/
buildUrl: function (pkg) {
var base = this.urls.base;
return base + [pkg.name, pkg.version, pkg.filename || pkg.name].join('/');
},
/**
* Build a usable package object with versions
*/
buildPackage: function (pkg) {
return {
name: pkg.name,
url: this.buildUrl({
name: pkg.root,
version: pkg.version,
filename: pkg.files.minified
}),
versions: pkg.versions.reduce(function (memo, version) {
memo[version] = this.buildUrl({
name: pkg.root,
version: version,
filename: pkg.files.minified
});
return memo;
}.bind(this), {})
};
},
/**
* Extract package name and version from search term
*/
extractTerm: function (term) {
var segments = term.split('@');
return {
name: segments[0],
version: segments[1]
};
},
/**
* Get the correct version for a given package
*/
getVersion: function (version, pkg) {
if (!version) { return pkg; }
if (pkg.url.indexOf(version) !== -1) { return pkg; }
if (!pkg.versions[version]) { return pkg; }
pkg.url = pkg.versions[version];
pkg.name = pkg.name + '@' + version;
return pkg;
},
/**
* Cached list of packages
* Why is this not cached to a file? Becuase the global tool should always go
* looking properly, but a module using cdnjs (like an API) will keep it in
* memory so it needs recaching after 24 hours.
*/
cache: null,
/**
* Grab the packages from the local cache, or cdnjs.
*/
packages: function (cb) {
// If we've got the packages cached use them, but only if it's not expired.
if (this.cache && this.cache.packages) {
if (moment().isBefore(this.cache.expires)) {
return cb(null, this.cache.packages, true);
}
}
// Grab some JSON from the cdnjs list
request
.get({ url: this.urls.packages, json:true }, function (err, res, body) {
if (err) { return cb(err); }
// The wrong thing came back, gtfo
if (!(body && body.packages)) { return cb(null, []); }
var data = transform(body.packages);
// Cache the good stuff, and set and expiry time
this.cache = {
packages: data,
expires: moment().add('hours', 24)
};
// Transform the packages into a useful form and send on back
return cb(null, data);
}.bind(this));
},
/**
* Search the packages list for an identifier. Loosey-goosey.
*/
search: function (term, cb) {
term = this.extractTerm(term);
this.packages(function (err, packages) {
if (err) { return cb(err); }
// Loosely search the names of the packages, then trasform them into a
// usable format.
var results = searchByName(term.name, packages).map(this.buildPackage.bind(this));
if (!results.length) { return cb(new Error("No matching packages found.")); }
return cb(null, results);
}.bind(this));
},
/**
* Get a URL for an exact identifier match.
*/
url: function (term, cb) {
term = this.extractTerm(term);
this.packages(function (err, packages) {
if (err) { return cb(err); }
var pkg = findByName(term.name, packages);
if (!pkg) { pkg = findByName(toggleExtension(term.name), packages); }
if (!pkg) { return cb(new Error("No such package found.")); }
var version = this.getVersion(term.version, this.buildPackage(pkg));
return cb(null, version);
}.bind(this));
}
};
// Export the API
module.exports = cdnjs;