2012-05-02 10:02:47 +00:00
|
|
|
/*\
|
2012-05-03 20:47:16 +00:00
|
|
|
title: $:/core/modules/commander.js
|
2012-05-02 10:02:47 +00:00
|
|
|
type: application/javascript
|
|
|
|
module-type: global
|
|
|
|
|
|
|
|
The $tw.Commander class is a command interpreter
|
|
|
|
|
|
|
|
\*/
|
|
|
|
(function(){
|
|
|
|
|
|
|
|
/*jslint node: true, browser: true */
|
2012-05-04 17:49:04 +00:00
|
|
|
/*global $tw: false */
|
2012-05-02 10:02:47 +00:00
|
|
|
"use strict";
|
|
|
|
|
|
|
|
/*
|
|
|
|
Parse a sequence of commands
|
|
|
|
commandTokens: an array of command string tokens
|
|
|
|
wiki: reference to the wiki store object
|
|
|
|
streams: {output:, error:}, each of which has a write(string) method
|
|
|
|
callback: a callback invoked as callback(err) where err is null if there was no error
|
|
|
|
*/
|
|
|
|
var Commander = function(commandTokens,callback,wiki,streams) {
|
2014-04-25 21:41:59 +00:00
|
|
|
var path = require("path");
|
2012-05-02 10:02:47 +00:00
|
|
|
this.commandTokens = commandTokens;
|
|
|
|
this.nextToken = 0;
|
|
|
|
this.callback = callback;
|
|
|
|
this.wiki = wiki;
|
|
|
|
this.streams = streams;
|
2014-04-27 07:28:21 +00:00
|
|
|
this.outputPath = path.resolve($tw.boot.wikiPath,$tw.config.wikiOutputSubDir);
|
2014-04-25 21:41:59 +00:00
|
|
|
};
|
|
|
|
|
2017-02-18 13:30:04 +00:00
|
|
|
/*
|
|
|
|
Log a string if verbose flag is set
|
|
|
|
*/
|
|
|
|
Commander.prototype.log = function(str) {
|
|
|
|
if(this.verbose) {
|
|
|
|
this.streams.output.write(str + "\n");
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
Write a string if verbose flag is set
|
|
|
|
*/
|
|
|
|
Commander.prototype.write = function(str) {
|
|
|
|
if(this.verbose) {
|
|
|
|
this.streams.output.write(str);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-04-25 21:41:59 +00:00
|
|
|
/*
|
|
|
|
Add a string of tokens to the command queue
|
|
|
|
*/
|
|
|
|
Commander.prototype.addCommandTokens = function(commandTokens) {
|
2014-06-24 11:09:10 +00:00
|
|
|
var params = commandTokens.slice(0);
|
|
|
|
params.unshift(0);
|
|
|
|
params.unshift(this.nextToken);
|
|
|
|
Array.prototype.splice.apply(this.commandTokens,params);
|
2012-05-02 10:02:47 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
Execute the sequence of commands and invoke a callback on completion
|
|
|
|
*/
|
|
|
|
Commander.prototype.execute = function() {
|
|
|
|
this.executeNextCommand();
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
Execute the next command in the sequence
|
|
|
|
*/
|
|
|
|
Commander.prototype.executeNextCommand = function() {
|
2012-05-02 16:27:33 +00:00
|
|
|
var self = this;
|
2012-05-02 10:02:47 +00:00
|
|
|
// Invoke the callback if there are no more commands
|
|
|
|
if(this.nextToken >= this.commandTokens.length) {
|
|
|
|
this.callback(null);
|
|
|
|
} else {
|
|
|
|
// Get and check the command token
|
|
|
|
var commandName = this.commandTokens[this.nextToken++];
|
|
|
|
if(commandName.substr(0,2) !== "--") {
|
2014-11-09 14:49:33 +00:00
|
|
|
this.callback("Missing command: " + commandName);
|
2012-05-02 10:02:47 +00:00
|
|
|
} else {
|
|
|
|
commandName = commandName.substr(2); // Trim off the --
|
|
|
|
// Accumulate the parameters to the command
|
|
|
|
var params = [];
|
|
|
|
while(this.nextToken < this.commandTokens.length &&
|
|
|
|
this.commandTokens[this.nextToken].substr(0,2) !== "--") {
|
|
|
|
params.push(this.commandTokens[this.nextToken++]);
|
|
|
|
}
|
|
|
|
// Get the command info
|
2012-05-02 16:27:33 +00:00
|
|
|
var command = $tw.commands[commandName],
|
|
|
|
c,err;
|
2012-05-02 10:02:47 +00:00
|
|
|
if(!command) {
|
|
|
|
this.callback("Unknown command: " + commandName);
|
|
|
|
} else {
|
2012-05-02 16:27:33 +00:00
|
|
|
if(this.verbose) {
|
|
|
|
this.streams.output.write("Executing command: " + commandName + " " + params.join(" ") + "\n");
|
|
|
|
}
|
2018-07-18 15:54:43 +00:00
|
|
|
// Parse named parameters if required
|
|
|
|
if(command.info.namedParameterMode) {
|
|
|
|
params = this.extractNamedParameters(params,command.info.mandatoryParameters);
|
|
|
|
if(typeof params === "string") {
|
|
|
|
return this.callback(params);
|
|
|
|
}
|
|
|
|
}
|
2012-05-02 10:02:47 +00:00
|
|
|
if(command.info.synchronous) {
|
2012-05-02 16:27:33 +00:00
|
|
|
// Synchronous command
|
|
|
|
c = new command.Command(params,this);
|
|
|
|
err = c.execute();
|
2012-05-02 14:02:58 +00:00
|
|
|
if(err) {
|
|
|
|
this.callback(err);
|
|
|
|
} else {
|
|
|
|
this.executeNextCommand();
|
|
|
|
}
|
2012-05-02 16:27:33 +00:00
|
|
|
} else {
|
|
|
|
// Asynchronous command
|
|
|
|
c = new command.Command(params,this,function(err) {
|
|
|
|
if(err) {
|
|
|
|
self.callback(err);
|
|
|
|
} else {
|
|
|
|
self.executeNextCommand();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
err = c.execute();
|
|
|
|
if(err) {
|
|
|
|
this.callback(err);
|
|
|
|
}
|
2012-05-02 10:02:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-07-18 15:54:43 +00:00
|
|
|
/*
|
|
|
|
Given an array of parameter strings `params` in name:value format, and an array of mandatory parameter names in `mandatoryParameters`, returns a hashmap of values or a string if error
|
|
|
|
*/
|
|
|
|
Commander.prototype.extractNamedParameters = function(params,mandatoryParameters) {
|
|
|
|
mandatoryParameters = mandatoryParameters || [];
|
|
|
|
var errors = [],
|
|
|
|
paramsByName = Object.create(null);
|
|
|
|
// Extract the parameters
|
|
|
|
$tw.utils.each(params,function(param) {
|
|
|
|
var index = param.indexOf("=");
|
|
|
|
if(index < 1) {
|
|
|
|
errors.push("malformed named parameter: '" + param + "'");
|
|
|
|
}
|
|
|
|
paramsByName[param.slice(0,index)] = $tw.utils.trim(param.slice(index+1));
|
|
|
|
});
|
|
|
|
// Check the mandatory parameters are present
|
|
|
|
$tw.utils.each(mandatoryParameters,function(mandatoryParameter) {
|
|
|
|
if(!$tw.utils.hop(paramsByName,mandatoryParameter)) {
|
|
|
|
errors.push("missing mandatory parameter: '" + mandatoryParameter + "'");
|
|
|
|
}
|
|
|
|
});
|
|
|
|
// Return any errors
|
|
|
|
if(errors.length > 0) {
|
|
|
|
return errors.join(" and\n");
|
|
|
|
} else {
|
2021-05-30 18:20:17 +00:00
|
|
|
return paramsByName;
|
2018-07-18 15:54:43 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2012-05-02 10:02:47 +00:00
|
|
|
Commander.initCommands = function(moduleType) {
|
|
|
|
moduleType = moduleType || "command";
|
|
|
|
$tw.commands = {};
|
2012-11-14 11:23:43 +00:00
|
|
|
$tw.modules.forEachModuleOfType(moduleType,function(title,module) {
|
|
|
|
var c = $tw.commands[module.info.name] = {};
|
|
|
|
// Add the methods defined by the module
|
|
|
|
for(var f in module) {
|
|
|
|
if($tw.utils.hop(module,f)) {
|
|
|
|
c[f] = module[f];
|
2012-05-02 10:02:47 +00:00
|
|
|
}
|
|
|
|
}
|
2012-11-14 11:23:43 +00:00
|
|
|
});
|
2012-05-02 10:02:47 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
exports.Commander = Commander;
|
|
|
|
|
|
|
|
})();
|