Files
meteor/tools/package-loader.js
2014-06-23 22:24:44 -07:00

121 lines
4.2 KiB
JavaScript

var fs = require('fs');
var path = require('path');
var _ = require('underscore');
var packageCache = require('./package-cache.js');
var catalog = require('./catalog.js');
var utils = require('./utils.js');
var buildmessage = require('./buildmessage.js');
var unipackage = require('./unipackage.js');
// options:
// - versions: a map from package name to the version to use. or null to only
// use local packages and ignore the package versions.
// - uniloadDir: if specified, versions should be null, and this loader will
// *only* load packages that are already unipackages and are in this
// directory
exports.PackageLoader = function (options) {
var self = this;
self.versions = options.versions || null;
self.uniloadDir = options.uniloadDir;
};
_.extend(exports.PackageLoader.prototype, {
// Given the name of a package, return a Unipackage object, or throw an
// error if the package wasn't included in the 'versions' passed on
// initalization or isn't available (for example, hasn't been
// downloaded yet).
//
// Options are:
// - throwOnError: if true (the default), throw an error if the
// package can't be found. (If false is passed for throwOnError,
// then return null if the package can't be found.) When called
// inside buildmessage.enterJob, however, instead of throwing an
// error it will record a build error and return a dummy (empty)
// package.
// XXX rename to throwOnNotFound
getPackage: function (name, options) {
var self = this;
options = options || {};
if (options.throwOnError === undefined) {
options.throwOnError = true;
}
var loadPath = self.getLoadPathForPackage(name);
if (! loadPath) {
if (options.throwOnError === false)
return null;
buildmessage.error("package not available: " + name);
// recover by returning a dummy (empty) package
var pkg = new unipackage.Unipackage;
pkg.initEmpty(name);
return pkg;
}
return packageCache.packageCache.loadPackageAtPath(name, loadPath);
},
containsPlugins: function (name) {
var self = this;
// We don't want to ever look at the catalog in the uniload case. We
// shouldn't ever care about plugins anyway, since uniload should never
// compile real packages from source (it sorta compiles the wrapper "load"
// package, which should avoid calling this function).
if (self.uniloadDir)
throw Error("called containsPlugins for uniload?");
var versionRecord;
if (self.versions === null) {
versionRecord = catalog.complete.getLatestVersion(name);
} else if (_.has(self.versions, name)) {
versionRecord = catalog.complete.getVersion(name, self.versions[name]);
} else {
throw new Error("no version specified for package " + name);
}
return versionRecord.containsPlugins;
},
// As getPackage, but returns the path of the package that would be
// loaded rather than loading the package, and does not take any
// options. Returns null if the package is not available.
//
// XXX it's a little unfortunate that we have two functions called
// getLoadPathForPackage, one on this object (which does not take a
// version) and one on Catalog (which does). Maybe rename them to
// getPackageLoadPath / getPackageVersionLoadPath?
getLoadPathForPackage: function (name) {
var self = this;
if (self.uniloadDir) {
var packagePath = path.join(self.uniloadDir, name);
if (!fs.existsSync(path.join(packagePath, 'unipackage.json'))) {
return null;
}
return packagePath;
}
if (self.versions && ! _.has(self.versions, name)) {
throw new Error("no version chosen for package " + name + "?");
}
var version;
if (self.versions) {
version = self.versions[name];
} else {
version = null;
}
return catalog.complete.getLoadPathForPackage(name, version);
},
// Given a package name like "ddp" and an architecture, get the unibuild of
// that package at the right architecture.
getUnibuild: function (packageName, arch) {
var self = this;
var pkg = self.getPackage(packageName, { throwOnError: true });
return pkg.getUnibuildAtArch(arch);
}
});