1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-11-30 05:19:57 +00:00

Add support for prompts within build scripts

This commit is contained in:
Jeremy Ruston 2024-10-19 15:54:31 +01:00
parent 50118dbe13
commit 4a6501778a
4 changed files with 178 additions and 69 deletions

View File

@ -16,7 +16,7 @@ The $tw.Commander class is a command interpreter
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
streams: {output:, input:, error:}
callback: a callback invoked as callback(err) where err is null if there was no error
*/
var Commander = function(commandTokens,callback,wiki,streams) {
@ -65,52 +65,95 @@ Commander.prototype.execute = function() {
};
/*
Returns the next string token without consuming it, or null if there are none left
Returns the next string token without consuming it, or null if there are none left. Callback invoked(err,data)
*/
Commander.prototype.peekNextToken = function() {
Commander.prototype.peekNextToken = function(callback) {
var self = this;
if(this.nextToken >= this.commandTokens.length) {
return null;
return callback(null,null);
} else {
return this.stringifyToken(this.nextToken);
return this.stringifyToken(this.nextToken,function(err,data) {
if(!err) {
// Save the stringified token for next time so that we don't run prompts twice
self.commandTokens[self.nextToken] = data;
}
callback(err,data);
});
}
};
/*
Returns and consumes the next string token, or null if there are none left
Returns and consumes the next string token, or null if there are none left. Callback invoked(err,data)
*/
Commander.prototype.getNextToken = function() {
Commander.prototype.getNextToken = function(callback) {
if(this.nextToken >= this.commandTokens.length) {
return null;
return callback(null,null);
} else {
return this.stringifyToken(this.nextToken++);
return this.stringifyToken(this.nextToken++,callback);
}
};
/*
Returns and consumes the string tokens until the end of the token stream or the first token that starts with "--".
Callback invoked(err,tokenArray)
*/
Commander.prototype.getTokensUntilCommand = function(callback) {
var self = this,
tokens = [];
function processNextToken() {
self.peekNextToken(function(err,data) {
if(err) {
return callback(err);
}
if(!data || data.substr(0,2) === "--") {
return callback(null,tokens);
} else {
self.getNextToken(function(err,data) {
if(err) {
return callback(err);
}
tokens.push(data);
processNextToken();
});
}
});
}
processNextToken();
};
/*
Returns a specified stringified token, or null if the index does not exist
Returns a specified stringified token, or null if the index does not exist. Callback invoked(err,data)
*/
Commander.prototype.stringifyToken = function(index) {
Commander.prototype.stringifyToken = function(index,callback) {
var self = this;
if(index >= this.commandTokens.length) {
return null;
return callback(null,null);
} else {
var token = this.commandTokens[index];
if(typeof token === "string") {
return token;
return callback(null,token);
} else if(typeof token === "object") {
switch(token.type) {
case "filter":
return this.wiki.filterTiddlers(token.text)[0] || "";
break;
return callback(null,this.wiki.filterTiddlers(token.text)[0] || "");
case "wikified":
return this.wiki.renderText("text/plain","text/vnd.tiddlywiki",token.text,{
return callback(null,this.wiki.renderText("text/plain","text/vnd.tiddlywiki",token.text,{
parseAsInline: false,
parentWidget: $tw.rootWidget
}));
case "prompt":
$tw.utils.terminalQuestion({
promptText: token.prompt || "Please enter a value",
defaultResult: token["default"] || "",
callback: function(err,userText) {
callback(err,userText);
},
input: self.streams.input,
output: self.streams.output,
});
break;
default:
throw "Unknown dynamic command token type: " + token.type;
break;
}
}
}
@ -122,62 +165,64 @@ Execute the next command in the sequence
Commander.prototype.executeNextCommand = function() {
var self = this;
// Get and check the command token
var commandName = this.getNextToken();
if(!commandName) {
return this.callback(null);
}
if(commandName.substr(0,2) !== "--") {
this.callback("Missing command: " + commandName);
} else {
commandName = commandName.substr(2); // Trim off the --
// Accumulate the parameters to the command
var params = [],
nextToken = this.peekNextToken();
while(typeof nextToken === "string" && nextToken.substr(0,2) !== "--") {
params.push(this.getNextToken());
nextToken = this.peekNextToken();
var commandName = this.getNextToken(function(err,commandName) {
if(err) {
return self.callback(err);
}
// Get the command info
var command = $tw.commands[commandName],
c,err;
if(!command) {
this.callback("Unknown command: " + commandName);
if(!commandName) {
return self.callback(null);
}
if(commandName.substr(0,2) !== "--") {
return self.callback("Missing command: " + commandName);
} else {
if(this.verbose) {
this.streams.output.write("Executing command: " + commandName + " " + params.join(" ") + "\n");
}
// Parse named parameters if required
if(command.info.namedParameterMode) {
params = this.extractNamedParameters(params,command.info.mandatoryParameters);
if(typeof params === "string") {
return this.callback(params);
}
}
if(command.info.synchronous) {
// Synchronous command
c = new command.Command(params,this);
err = c.execute();
commandName = commandName.substr(2); // Trim off the --
// Get the parameters to the command
self.getTokensUntilCommand(function(err,params) {
if(err) {
this.callback(err);
return self.callback(err);
}
var command = $tw.commands[commandName],
c,err;
if(!command) {
self.callback("Unknown command: " + commandName);
} else {
this.executeNextCommand();
}
} else {
// Asynchronous command
c = new command.Command(params,this,function(err) {
if(err) {
self.callback(err);
} else {
self.executeNextCommand();
if(self.verbose) {
self.streams.output.write("Executing command: " + commandName + " " + params.join(" ") + "\n");
}
// Parse named parameters if required
if(command.info.namedParameterMode) {
params = self.extractNamedParameters(params,command.info.mandatoryParameters);
if(typeof params === "string") {
return self.callback(params);
}
}
if(command.info.synchronous) {
// Synchronous command
c = new command.Command(params,self);
err = c.execute();
if(err) {
self.callback(err);
} else {
self.executeNextCommand();
}
} else {
// Asynchronous command
c = new command.Command(params,self,function(err) {
if(err) {
self.callback(err);
} else {
self.executeNextCommand();
}
});
err = c.execute();
if(err) {
self.callback(err);
}
}
});
err = c.execute();
if(err) {
this.callback(err);
}
}
});
}
}
});
};
/*

View File

@ -29,7 +29,11 @@ exports.startup = function(callback) {
callback();
},
$tw.wiki,
{output: process.stdout, error: process.stderr}
{
output: process.stdout,
input: process.stdin,
error: process.stderr
}
);
commander.execute();
};

View File

@ -21,6 +21,50 @@ exports.log = function(text,colour) {
console.log($tw.node ? exports.terminalColour(colour) + text + exports.terminalColour() : text);
};
/*
Prompts the user for input and handles the input using a callback function. Options:
promptText: Prompt text
defaultResult: Default result returned if the user types enter
callback: callback invoked (err,usertext)
input: optional input stream
output: optional output stream
*/
exports.terminalQuestion = function(options) {
var readline = require("readline"),
promptText = options.promptText,
defaultResult = options.defaultResult,
callback = options.callback,
inputStream = options.input || process.stdin,
outputStream = options.output || process.stdout;
var rl = readline.createInterface({
input: inputStream,
output: outputStream
});
// Handle errors on the input stream
rl.on("error",function(err) {
rl.close();
callback(err); // Pass the error to the callback
});
// Prompt user for input
var prompt = exports.terminalColourText(promptText,"yellow");
if(defaultResult) {
prompt += exports.terminalColourText(" (" + defaultResult + ")","blue");
}
prompt += exports.terminalColourText(": ");
rl.question(prompt,function(input) {
// Use default value for the empty string
if(input === "" && defaultResult) {
input = defaultResult;
}
callback(null,input); // Pass the input to the callback
rl.close();
});
};
exports.terminalColourText = function(text,colour) {
return exports.terminalColour(colour) + text + exports.terminalColour("reset");
};
exports.terminalColour = function(colour) {
if(!$tw.browser && $tw.node && process.stdout.isTTY) {
if(colour) {
@ -36,14 +80,24 @@ exports.terminalColour = function(colour) {
};
exports.terminalColourLookup = {
"reset": "0;0",
"black": "0;30",
"red": "0;31",
"green": "0;32",
"brown/orange": "0;33",
"yellow": "0;33",
"brown/orange": "0;33", // Backwards compatbility
"blue": "0;34",
"purple": "0;35",
"cyan": "0;36",
"light gray": "0;37"
"white": "0;37",
"gray": "0;90",
"light red": "0;91",
"light green": "0;92",
"light yellow": "0;93",
"light blue": "0;94",
"light purple": "0;95",
"light cyan": "0;96",
"light gray": "0;97"
};
/*

View File

@ -33,6 +33,12 @@
{
"type": "filter",
"text": "[<version>!match[5.3.6-prerelease]then[text/html]else[text/plain]]"
},
"dinghy",
{
"type": "prompt",
"prompt": "Please enter the name of your new wiki",
"default": "untitled"
}
],
"index": [