Some other tweaks to the API.

This commit is contained in:
André Cruz
2013-05-22 22:58:04 +01:00
parent 60ecceafec
commit 9f6bf62efc
7 changed files with 190 additions and 153 deletions

View File

@@ -17,68 +17,75 @@ var Project = function (options) {
this._manager = new Manager(options);
};
Project.prototype.install = function (targets) {
// -----------------
Project.prototype.install = function (endpoints) {
var repairResult;
var that = this;
var repairDissected;
// If already working, error out
if (this._working) {
return Q.reject(createError('Already working', 'EWORKING'));
}
// If no targets were specified, simply repair the project if necessary
// If no endpoints were specified, simply repair the project
// Note that we also repair incompatible packages
if (!targets) {
if (!endpoints) {
return this._repair(true)
.fin(function () {
that._working = false;
}.bind(this));
});
}
// Start by repairing the project, installing any missing packages
// Start by repairing the project, installing only missing packages
return this._repair()
// Analyse the project
.then(function (dissected) {
repairDissected = dissected;
.then(function (result) {
repairResult = result;
return that._analyse();
})
// Decide which dependencies should be fetched and the ones
// that are already resolved
.spread(function (json, tree, flattened) {
var unresolved = {};
var resolved = {};
var targetNames = {};
var targets = [];
var installed = {};
// Mark targets as unresolved
targets.forEach(function (target) {
unresolved[target.name] = endpointParser.decompose(target);
// Mark targets
endpoints.forEach(function (target) {
var decEndpoint = endpointParser.decompose(target);
targetNames[decEndpoint.name] = true;
targets.push(decEndpoint);
});
// Mark every package from the tree as resolved
// Mark every package from the tree as installed
// if they are not a target or a non-shared descendant of a target
// TODO: We should do traverse the tree (vertically) and
// TODO: We should traverse the tree (deep first) and
// add each leaf to the resolved
// If a leaf is a target, we abort traversal of it
resolved = mout.object.filter(flattened, function (decEndpoint, name) {
return !unresolved[name];
mout.object.forOwn(flattened, function (decEndpoint, name) {
if (targetNames[name]) {
return;
}
installed[name] = decEndpoint.pkgMeta;
});
// Configure the manager with the unresolved and resolved endpoints
// And kick in the resolve process
// Configure the manager and kick in the resolve process
return that._manager
.configure(unresolved, resolved)
.configure(targets, installed)
.resolve()
// Install resolved ones
.then(function () {
return that._manager.install();
})
// Resolve with the repair and install dissection
.then(function (dissected) {
return mout.object.fillIn(dissected, repairDissected);
// Resolve the promise with the repair and install results,
// by merging them together
.then(function (result) {
return mout.object.fillIn(result, repairResult);
});
})
.fin(function () {
that._working = false;
}.bind(this));
});
};
Project.prototype.update = function (names) {
@@ -107,23 +114,22 @@ Project.prototype._analyse = function () {
])
.spread(function (json, installed) {
var root;
var flattened = installed;
root = {
name: json.name,
source: this._config.cwd,
target: json.version || '*',
json: json,
dir: this._config.cwd
pkgMeta: json
};
// Restore the original dependencies cross-references,
// that is, the parent-child relationships
this._restoreNode(root, installed);
this._restoreNode(root, flattened);
// Do the same for the dev dependencies
if (!this._options.production) {
this._restoreNode(root, installed, 'devDependencies');
this._restoreNode(root, flattened, 'devDependencies');
}
return [json, root, installed];
return [json, root, flattened];
}.bind(this));
};
@@ -132,21 +138,21 @@ Project.prototype._repair = function (incompatible) {
return this._analyse()
.spread(function (json, tree, flattened) {
var unresolved = {};
var resolved = {};
var targets = [];
var installed = {};
var isBroken = false;
// Figure out which are the missing/incompatible ones
// by parsing the flattened tree
mout.object.forOwn(flattened, function (decEndpoint, name) {
if (decEndpoint.missing) {
unresolved[name] = decEndpoint;
targets.push(decEndpoint);
isBroken = true;
} else if (incompatible && decEndpoint.incompatible) {
unresolved[name] = decEndpoint;
targets.push(decEndpoint);
isBroken = true;
} else {
resolved[name] = decEndpoint;
installed[name] = decEndpoint.pkgMeta;
}
});
@@ -155,10 +161,9 @@ Project.prototype._repair = function (incompatible) {
return {};
}
// Configure the manager with the unresolved and resolved endpoints
// And kick in the resolve process
// Configure the manager and kick in the resolve process
return that._manager
.configure(unresolved, resolved)
.configure(targets, installed)
.resolve()
// Install after resolve
.then(function () {
@@ -213,6 +218,7 @@ Project.prototype._readInstalled = function () {
})
.then(function (filenames) {
var promises = [];
var decEndpoints = {};
// Foreach bower.json found
filenames.forEach(function (filename) {
@@ -222,16 +228,11 @@ Project.prototype._readInstalled = function () {
// Read package metadata
promise = Q.nfcall(fs.readFile, path.join(componentsDir, filename))
.then(function (contents) {
var json = JSON.parse(contents.toString());
var dir = path.join(componentsDir, name);
var pkgMeta = JSON.parse(contents.toString());
// Set decomposed endpoint manually
return {
decEndpoints[name] = {
name: name,
source: dir,
target: json.version || '*',
json: json,
dir: dir
pkgMeta: pkgMeta
};
});
@@ -239,21 +240,15 @@ Project.prototype._readInstalled = function () {
});
// Wait until all files have been read
// to form the final object of decomposed endpoints
// and resolve with the decomposed endpoints
return Q.all(promises)
.then(function (locals) {
var decEndpoints = {};
locals.forEach(function (decEndpoint) {
decEndpoints[decEndpoint.name] = decEndpoint;
});
.then(function () {
return decEndpoints;
});
});
};
Project.prototype._restoreNode = function (node, locals, jsonKey) {
Project.prototype._restoreNode = function (node, flattened, jsonKey) {
// Do not restore if already processed or if the node is
// missing or incompatible
if (node.dependencies || node.missing || node.incompatible) {
@@ -261,20 +256,23 @@ Project.prototype._restoreNode = function (node, locals, jsonKey) {
}
node.dependencies = {};
node.dependants = {};
mout.object.forOwn(node.json[jsonKey || 'dependencies'], function (value, key) {
var local = locals[key];
mout.object.forOwn(node.pkgMeta[jsonKey || 'dependencies'], function (value, key) {
var local = flattened[key];
var json = endpointParser.json2decomposed(key, value);
// Check if the dependency is installed
// Check if the dependency is not installed
if (!local) {
local = endpointParser.json2decomposed(key, value);
local = json;
local.missing = true;
locals[key] = local;
// If so, also check if it's compatible
} else if (!this._manager.areCompatible(local, json)) {
flattened[key] = local;
// Even if it is installed, check if it's compatible
} else if (!local.incompatible && !this._manager.areCompatible(local.pkgMeta.version || '*', json.target)) {
json.pkgMeta = local.pkgMeta;
local = json;
local.incompatible = true;
locals[key] = json;
flattened[key] = local;
}
// Cross reference
@@ -283,7 +281,7 @@ Project.prototype._restoreNode = function (node, locals, jsonKey) {
local.dependants[node.name] = node;
// Call restore for this dependency
this._restoreNode(local, locals);
this._restoreNode(local, flattened);
}, this);
};