1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2025-03-02 01:40:03 +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 Parse a sequence of commands
commandTokens: an array of command string tokens commandTokens: an array of command string tokens
wiki: reference to the wiki store object 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 callback: a callback invoked as callback(err) where err is null if there was no error
*/ */
var Commander = function(commandTokens,callback,wiki,streams) { 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) { if(this.nextToken >= this.commandTokens.length) {
return null; return callback(null,null);
} else { } 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) { if(this.nextToken >= this.commandTokens.length) {
return null; return callback(null,null);
} else { } 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) { if(index >= this.commandTokens.length) {
return null; return callback(null,null);
} else { } else {
var token = this.commandTokens[index]; var token = this.commandTokens[index];
if(typeof token === "string") { if(typeof token === "string") {
return token; return callback(null,token);
} else if(typeof token === "object") { } else if(typeof token === "object") {
switch(token.type) { switch(token.type) {
case "filter": case "filter":
return this.wiki.filterTiddlers(token.text)[0] || ""; return callback(null,this.wiki.filterTiddlers(token.text)[0] || "");
break;
case "wikified": 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, parseAsInline: false,
parentWidget: $tw.rootWidget 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; break;
default: default:
throw "Unknown dynamic command token type: " + token.type; throw "Unknown dynamic command token type: " + token.type;
break;
} }
} }
} }
@ -122,49 +165,49 @@ Execute the next command in the sequence
Commander.prototype.executeNextCommand = function() { Commander.prototype.executeNextCommand = function() {
var self = this; var self = this;
// Get and check the command token // Get and check the command token
var commandName = this.getNextToken(); var commandName = this.getNextToken(function(err,commandName) {
if(err) {
return self.callback(err);
}
if(!commandName) { if(!commandName) {
return this.callback(null); return self.callback(null);
} }
if(commandName.substr(0,2) !== "--") { if(commandName.substr(0,2) !== "--") {
this.callback("Missing command: " + commandName); return self.callback("Missing command: " + commandName);
} else { } else {
commandName = commandName.substr(2); // Trim off the -- commandName = commandName.substr(2); // Trim off the --
// Accumulate the parameters to the command // Get the parameters to the command
var params = [], self.getTokensUntilCommand(function(err,params) {
nextToken = this.peekNextToken(); if(err) {
while(typeof nextToken === "string" && nextToken.substr(0,2) !== "--") { return self.callback(err);
params.push(this.getNextToken());
nextToken = this.peekNextToken();
} }
// Get the command info
var command = $tw.commands[commandName], var command = $tw.commands[commandName],
c,err; c,err;
if(!command) { if(!command) {
this.callback("Unknown command: " + commandName); self.callback("Unknown command: " + commandName);
} else { } else {
if(this.verbose) { if(self.verbose) {
this.streams.output.write("Executing command: " + commandName + " " + params.join(" ") + "\n"); self.streams.output.write("Executing command: " + commandName + " " + params.join(" ") + "\n");
} }
// Parse named parameters if required // Parse named parameters if required
if(command.info.namedParameterMode) { if(command.info.namedParameterMode) {
params = this.extractNamedParameters(params,command.info.mandatoryParameters); params = self.extractNamedParameters(params,command.info.mandatoryParameters);
if(typeof params === "string") { if(typeof params === "string") {
return this.callback(params); return self.callback(params);
} }
} }
if(command.info.synchronous) { if(command.info.synchronous) {
// Synchronous command // Synchronous command
c = new command.Command(params,this); c = new command.Command(params,self);
err = c.execute(); err = c.execute();
if(err) { if(err) {
this.callback(err); self.callback(err);
} else { } else {
this.executeNextCommand(); self.executeNextCommand();
} }
} else { } else {
// Asynchronous command // Asynchronous command
c = new command.Command(params,this,function(err) { c = new command.Command(params,self,function(err) {
if(err) { if(err) {
self.callback(err); self.callback(err);
} else { } else {
@ -173,11 +216,13 @@ Commander.prototype.executeNextCommand = function() {
}); });
err = c.execute(); err = c.execute();
if(err) { if(err) {
this.callback(err); self.callback(err);
} }
} }
} }
});
} }
});
}; };
/* /*

View File

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

View File

@ -21,6 +21,50 @@ exports.log = function(text,colour) {
console.log($tw.node ? exports.terminalColour(colour) + text + exports.terminalColour() : text); 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) { exports.terminalColour = function(colour) {
if(!$tw.browser && $tw.node && process.stdout.isTTY) { if(!$tw.browser && $tw.node && process.stdout.isTTY) {
if(colour) { if(colour) {
@ -36,14 +80,24 @@ exports.terminalColour = function(colour) {
}; };
exports.terminalColourLookup = { exports.terminalColourLookup = {
"reset": "0;0",
"black": "0;30", "black": "0;30",
"red": "0;31", "red": "0;31",
"green": "0;32", "green": "0;32",
"brown/orange": "0;33", "yellow": "0;33",
"brown/orange": "0;33", // Backwards compatbility
"blue": "0;34", "blue": "0;34",
"purple": "0;35", "purple": "0;35",
"cyan": "0;36", "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", "type": "filter",
"text": "[<version>!match[5.3.6-prerelease]then[text/html]else[text/plain]]" "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": [ "index": [