mirror of
				https://github.com/Jermolene/TiddlyWiki5
				synced 2025-10-31 15:42:59 +00:00 
			
		
		
		
	Add support for prompts within build scripts
This commit is contained in:
		| @@ -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,62 +165,64 @@ 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(!commandName) { | 		if(err) { | ||||||
| 		return this.callback(null); | 			return self.callback(err); | ||||||
| 	} |  | ||||||
| 	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(); |  | ||||||
| 		} | 		} | ||||||
| 		// Get the command info | 		if(!commandName) { | ||||||
| 		var command = $tw.commands[commandName], | 			return self.callback(null); | ||||||
| 			c,err; | 		} | ||||||
| 		if(!command) { | 		if(commandName.substr(0,2) !== "--") { | ||||||
| 			this.callback("Unknown command: " + commandName); | 			return self.callback("Missing command: " + commandName); | ||||||
| 		} else { | 		} else { | ||||||
| 			if(this.verbose) { | 			commandName = commandName.substr(2); // Trim off the -- | ||||||
| 				this.streams.output.write("Executing command: " + commandName + " " + params.join(" ") + "\n"); | 			// Get the parameters to the command | ||||||
| 			} | 			self.getTokensUntilCommand(function(err,params) { | ||||||
| 			// 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(); |  | ||||||
| 				if(err) { | 				if(err) { | ||||||
| 					this.callback(err); | 					return self.callback(err); | ||||||
|  | 				} | ||||||
|  | 				var command = $tw.commands[commandName], | ||||||
|  | 					c,err; | ||||||
|  | 				if(!command) { | ||||||
|  | 					self.callback("Unknown command: " + commandName); | ||||||
| 				} else { | 				} else { | ||||||
| 					this.executeNextCommand(); | 					if(self.verbose) { | ||||||
| 				} | 						self.streams.output.write("Executing command: " + commandName + " " + params.join(" ") + "\n"); | ||||||
| 			} else { | 					} | ||||||
| 				// Asynchronous command | 					// Parse named parameters if required | ||||||
| 				c = new command.Command(params,this,function(err) { | 					if(command.info.namedParameterMode) { | ||||||
| 					if(err) { | 						params = self.extractNamedParameters(params,command.info.mandatoryParameters); | ||||||
| 						self.callback(err); | 						if(typeof params === "string") { | ||||||
| 					} else { | 							return self.callback(params); | ||||||
| 						self.executeNextCommand(); | 						} | ||||||
|  | 					} | ||||||
|  | 					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); |  | ||||||
| 				} | 				} | ||||||
| 			} | 			}); | ||||||
| 		} | 		} | ||||||
| 	} | 	}); | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /* | /* | ||||||
|   | |||||||
| @@ -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(); | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -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" | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /* | /* | ||||||
|   | |||||||
| @@ -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": [ | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Jeremy Ruston
					Jeremy Ruston