cordova-check-plugins
Version:
A CLI tool to check for updates / manage updating Cordova plugins.
499 lines (452 loc) • 17.6 kB
JavaScript
var update = (function(){
/**********
* Modules
**********/
// Core
var path = require('path');
var exec = require('child_process').exec;
// lib
var logger = require('./logger.js')();
var errorHandler = require('./errorHandler.js')();
var progress = require('./progress.js')();
var local = require('./local.js')();
// 3rd party
try{
var fs = require('fs-extra');
var _ = require('lodash');
var inquirer = require('inquirer');
var plugman = require('plugman');
}catch(e){
errorHandler.handleFatalException(e, "Failed to acquire module dependencies");
}
/**********************
* Internal properties
*********************/
var update = {};
var opts;
var localCordova = "node node_modules/cordova/bin/cordova";
var globalCordova = "cordova";
var globalPhonegap = "phonegap";
/**********************
* Internal functions
*********************/
function updateModeIsPluginIds(){
return opts.updateMode && opts.updateMode !== "auto" && opts.updateMode !== "interactive" && opts.updateMode !== "none" && opts.updateMode !== null;
}
function resolveCliCommand(cb){
var _opts = {};
if(opts.cwd) _opts.cwd = opts.cwd;
function resolveLocalCordova(){
exec(localCordova + ' -v', _opts, function(err, stdout, stderr) {
err = err || stderr;
if(err){
logger.verbose("local cordova command not found - checking for global cordova.\n\n"+err);
resolveGlobalCordova();
}else{
cb(localCordova);
}
});
}
function resolveGlobalCordova(){
exec(globalCordova + ' -v', _opts, function(err, stdout, stderr) {
err = err || stderr;
if(err){
logger.verbose("global cordova command not found - checking for global phonegap.\n\n"+err);
resolveGlobalPhonegap();
}else{
cb(globalCordova);
}
});
}
function resolveGlobalPhonegap(){
exec(globalPhonegap + ' -v', _opts, function(err, stdout, stderr) {
err = err || stderr;
if(err){
errorHandler.handleFatalError( "Failed to find local/global cordova or global phonegap CLI command when listing installed plugins - ensure you have cordova npm module installed either locally in your project folder or globally.\n\n"+err);
}else{
cb(globalPhonegap);
}
});
}
resolveLocalCordova();
}
function updatePlugin(plugin, complete){
var cliCommand;
logger.log("\n");
progress.start("Updating '"+plugin.id+"'");
function finish(result){
progress.end();
complete(result);
}
function add(){
var pluginSource;
if(plugin.source.type === "git"){
pluginSource = plugin.source.url;
if(plugin.source.ref){
pluginSource += '#'+plugin.source.ref;
}
}else if(plugin.source.type === "local"){
pluginSource = plugin.source.path;
}else if(plugin.source.type === "registry"){
pluginSource = plugin.source.id;
if(opts.unconstrainVersions && pluginSource.match('@')){
pluginSource = pluginSource.split('@')[0] + '@' + plugin.target;
}
if(pluginSource.match('@>=')){
pluginSource = pluginSource.split('@')[0] + '@*';
}
}else{
logger.error("Unknown plugin source for '" + plugin.id+"'");
return finish(-1);
}
var args = '';
if(opts.nosave){
args += ' --nosave';
}else{
args += ' --save';
}
if(opts.nofetch){
args += ' --nofetch';
}else{
args += ' --fetch';
}
for(var name in plugin.variables){
args += ' --variable '+name+'="'+plugin.variables[name]+'"';
}
updateCommand = cliCommand+' plugin add '+pluginSource+args;
logger.verbose('Update command: '+updateCommand);
exec(updateCommand, {cwd: opts.cwd}, function(err, stdout, stderr) {
err = err || stderr;
if(err){
var msg = "\nError adding plugin '"+plugin.id+"'" + "\n\n" + err;
logger.error(msg);
finish(-1);
return;
}
logger.verbose("Re-added plugin '"+plugin.id+"'");
finish(0);
});
}
resolveCliCommand(function(command){
cliCommand = command;
if(plugin.installed){
remove(plugin.id, function(err){
if(err !== 0){
return finish(-1);
}else{
add();
}
});
}else{
add();
}
});
}
function updatePlugins(plugins, cb){
var pluginIds = [];
plugins.forEach(function(plugin){
pluginIds.push(plugin.id);
total = pluginIds.length;
});
logger.verbose("Updating plugins: "+pluginIds.join(", "));
var success = true;
function nextPlugin(){
if(plugins.length === 0){
cb(success);
return;
}
var plugin = plugins.pop();
updatePlugin(plugin, function(result){
updatedPlugin(plugin, result);
if(result === -1 && success) success = false;
nextPlugin();
});
}
nextPlugin();
}
function updatedPlugin(plugin, result){
if(result === 0){
logger.log("\nUpdated '"+plugin.id+"'"+" from "+(plugin.installed || "N/A")+" to "+plugin.target);
}else{
var msg = "Failed to update plugin '"+plugin.id+"'";
logger.error(msg);
}
}
function updateInteractive(plugins){
logger.verbose("Interactive update started");
function finished(){
logger.log("\nInteractive update complete".green);
}
function nextPlugin(){
if(plugins.length === 0){
finished();
return;
}
var plugin = plugins.pop();
inquirer.prompt([
{
type: "expand",
message: "Update '"+plugin.id+" from "+plugin.installed+" to "+plugin.target+"?",
name: "choice",
choices: [
{
key: "y",
name: "Yes",
value: "yes"
},
{
key: "n",
name: "No",
value: "no"
},
{
key: "a",
name: "All",
value: "all"
},
new inquirer.Separator(),
{
key: "x",
name: "Abort",
value: "abort"
}
]
}
], function(answer){
switch(answer.choice){
case "yes":
updatePlugin(plugin, nextPlugin);
break;
case "no":
nextPlugin();
break;
case "all":
plugins.push(plugin);
updatePlugins(plugins, finished);
break;
case "abort":
finished();
break;
}
});
}
nextPlugin();
}
function forceRemove(pluginId){
logger.verbose("Forcibly removing plugin '"+pluginId+"'");
try{
// remove from platforms/
var platforms = local.getPlatforms();
_.each(platforms, function (platform) {
plugman.raw.uninstall(platform, "platforms/"+platform, pluginId, "plugins", {
www_dir: "www",
force: true
});
});
// remove from plugins/
rmdirRfSync(path.join(local.PLUGINS_DIR, pluginId));
var complete = function (){
logger.verbose("Forcibly removed plugin '"+pluginId+"'");
};
var processConfigXml = function (){
if(!opts.nosave){
// remove from config.xml
local.readConfigXmlAsJs(function(js){
var _plugins = js['widget']['plugin'];
if(_plugins && _plugins.length > 0){
var deleted = false;
for(var i=0; i<_plugins.length; i++){
var _plugin = _plugins[i].$;
var name = _plugin.name;
if(name === pluginId){
delete _plugins[i];
deleted = true;
break;
}
}
if(deleted){
local.writeConfigXmlFromJs(js);
logger.verbose("Removed plugin entry for '"+pluginId+"' from config.xml");
}
}
complete();
});
}else{
complete();
}
};
var processPackageJson = function(){
// remove from package.json
local.readPackageJson(function(err, json){
if(err){
logger.error("Failed to read package.json to remove plugin entry for '"+pluginId+"': "+err);
return processConfigXml();
}
if(json.dependencies && json.dependencies[pluginId]){
delete json.dependencies[pluginId];
if(json.cordova && json.cordova.plugins && json.cordova.plugins[pluginId]){
delete json.cordova.plugins[pluginId];
}
local.writePackageJson(json, function(err){
if(err){
logger.error("Failed to write package.json after removing plugin entry for '"+pluginId+"': "+err);
}else{
logger.verbose("Removed plugin entry for '"+pluginId+"' from package.json");
}
processConfigXml();
});
}else{
processConfigXml();
}
});
};
var processFetchJson = function(){
// remove from fetch.json
local.readFetchJson(function(err, json){
if(err){
logger.error("Failed to read fetch.json to remove plugin entry for '"+pluginId+"': "+err);
return processPackageJson();
}
if(json[pluginId]){
delete json[pluginId];
local.writeFetchJson(json, function(err){
if(err){
logger.error("Failed to write fetch.json after removing plugin entry for '"+pluginId+"': "+err);
}else{
logger.verbose("Removed plugin entry for '"+pluginId+"' from fetch.json");
}
processPackageJson();
});
}else{
processPackageJson();
}
});
};
processFetchJson();
}catch(e){
logger.error("Failed to forcibly removed plugin '"+pluginId+"': "+e.message);
}
}
function remove (pluginId, callback){
resolveCliCommand(function(cliCommand){
var command = cliCommand+' plugin rm '+pluginId;
if(opts.nosave){
command += ' --nosave';
}else{
command += ' --save';
}
exec(command, {cwd: opts.cwd}, function(err, stdout, stderr) {
err = err || stderr;
if(err){
var msg = "\nError removing plugin '"+pluginId+"'" + "\n\n" + err;
if(opts.force){
logger.verbose(msg);
forceRemove(pluginId);
return callback(0);
}else{
logger.error(msg);
return callback(-1);
}
}
logger.verbose("Removed plugin '"+pluginId+"'");
callback(0);
});
});
}
var rmdirRfSync; rmdirRfSync = function(_path) {
var files = [];
if( fs.existsSync(path.resolve(opts.cwd, _path)) ) {
files = fs.readdirSync(path.resolve(opts.cwd, _path));
files.forEach(function(file,index){
var curPath = path.resolve(opts.cwd, _path + "/" + file);
if(fs.lstatSync(curPath).isDirectory()) {
rmdirRfSync(curPath); // recurse
} else { // delete file
fs.unlinkSync(curPath);
}
});
fs.rmdirSync(path.resolve(opts.cwd, _path));
}
};
/************
* Public API
************/
/**
* @param {array} plugins - list of all known plugins
* @param {array} outdated - list of outdated plugins
* @param {object} _opts - update options
*/
update.doUpdate = function (plugins, outdated, _opts){
opts = _opts;
if(opts.updateMode === "auto" && outdated){
updatePlugins(outdated, function(success){
if(success){
logger.log("\nAutomatically updated all outdated plugins".green);
}else{
logger.log("\nFailed to update all outdated plugins".yellow);
}
});
}else if(opts.updateMode === "interactive" && outdated){
updateInteractive(outdated);
}
else if(updateModeIsPluginIds()){
var pluginIds, valid = true, specifiedPlugins = [];
logger.verbose("opts.updateMode: "+opts.updateMode);
if(opts.updateMode.match && opts.updateMode.match(/\ /)){
logger.debug("opts.updateMode is multiple ");
pluginIds = opts.updateMode.split(' ');
}else{
logger.debug("opts.updateMode is single ");
pluginIds = [opts.updateMode];
}
pluginIds.forEach(function(pluginId){
if(!plugins[pluginId]){
valid = false;
return logger.warn("Cannot update plugin '"+pluginId+"' as it is not installed in the project");
}
if(plugins[pluginId].status !== "newer-target" && (opts.target !== "config" || !opts.allowDowndate)){
valid = false;
return logger.warn("Cannot update plugin '"+pluginId+"' as no newer target version is available");
}
specifiedPlugins.push(plugins[pluginId]);
});
updatePlugins(specifiedPlugins, function(success){
if(success && valid){
logger.log("\Successfully updated all specified plugins".green);
}else{
logger.log("\nFailed to update all specified plugins".yellow);
}
});
}
};
update.removeAll = function(plugins, _opts){
opts = _opts;
var succeededForAll = true;
var pluginsRemaining = plugins.length;
function finish(){
progress.end();
if(succeededForAll){
logger.log("\Successfully removed all installed plugins".green);
}else{
logger.log("\nFailed to remove all installed plugins".yellow);
}
}
progress.start("Removing all plugins");
plugins.forEach(function(plugin){
remove(plugin.id, function(res){
if(res !== 0){
succeededForAll = false;
}
pluginsRemaining--;
if(pluginsRemaining === 0){
finish();
}
});
});
};
return update;
})();
module.exports = function(){
return update;
};