├── README.md ├── index.js └── package.json /README.md: -------------------------------------------------------------------------------- 1 | interactive-script 2 | ================== 3 | 4 | Need to write a simple interactive terminal script in node? Dealing with [readline](https://nodejs.org/api/readline.html) can be overwhelming and result in [callback hell](http://callbackhell.com/). 5 | 6 | There's a simpler way that easier to write! 7 | 8 | To get started, we need to install a few dependencies: 9 | 10 | ```bash 11 | npm install -g interactive-script async-to-gen minimist 12 | ``` 13 | 14 | Now let's write `myScript.js`: 15 | 16 | ```js 17 | var interactiveScript = require('interactive-script'); 18 | var minimist = require('minimist'); 19 | 20 | interactiveScript(async (say, ask) => { 21 | const { pirate } = minimist(process.argv.slice(2)) 22 | 23 | say(pirate ? 'Avast, me hearty!' : 'Hello there') 24 | 25 | const name = await ask(pirate ? 'Whats ye name? ' : 'Who are you? ') 26 | 27 | say('HI THERE: ' + name.toUpperCase()) 28 | }) 29 | ``` 30 | 31 | You're given two functions: 32 | 33 | - Print to the screen with `say`. 34 | - Prompt and wait for a response with `ask`. 35 | 36 | You may have noticed that this script uses an [async function](https://github.com/tc39/ecmascript-asyncawait) 37 | which is not yet available out of the box in node.js. However you can install 38 | and use [async-node](https://github.com/leebyron/async-to-gen) to take 39 | advantage today. 40 | 41 | Let's run this script: 42 | 43 | ```bash 44 | $> async-node myScript.js --pirate 45 | Avast, me hearty! 46 | Whats ye name? Lee 47 | HI THERE: LEE 48 | $> 49 | ``` 50 | 51 | # Tastes great with: 52 | 53 | [minimist](https://github.com/substack/minimist): Read the arguments provided to 54 | your script from the terminal. 55 | 56 | [colors](https://github.com/marak/colors.js/): Ask and say things in a rainbow 57 | of colors for better legibility. 58 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var readline = require('readline') 2 | var util = require('util') 3 | 4 | function runInteractiveScript(fn) { 5 | var rl = readline.createInterface({ 6 | input: process.stdin, 7 | output: process.stdout 8 | }) 9 | 10 | /** 11 | * statement is formatted if not a string. 12 | * key is the second argument to readline.Interface#write 13 | */ 14 | function say(statement, key) { 15 | var output = statement == null || typeof statement === 'string' ? 16 | statement : 17 | util.inspect(statement, { colors: true, depth: 10 }) 18 | rl.write(output + '\n', key) 19 | } 20 | 21 | /** 22 | * query is prompted on the same line 23 | * Yn: return boolean defaulting to true 24 | * yN: return boolean defaulting to false 25 | */ 26 | function ask(query, yN) { 27 | return new Promise(function (resolve) { 28 | if (yN === 'Yn') { 29 | query += '[Yn] '; 30 | } else if (yN) { 31 | query += '[yN] '; 32 | } 33 | rl.question(query, function (answer) { 34 | if (yN === 'Yn') { 35 | resolve(answer[0] !== 'n' && answer[0] !== 'N') 36 | } else if (yN) { 37 | resolve(answer[0] === 'y' || answer[0] === 'Y') 38 | } else { 39 | resolve(answer) 40 | } 41 | }) 42 | }) 43 | } 44 | 45 | fn(say, ask).then( 46 | function (code) { 47 | rl.close() 48 | process.exit(code || 0) 49 | }, 50 | function (error) { 51 | rl.close() 52 | console.error(error) 53 | process.exit(process.exitCode || 1) 54 | } 55 | ) 56 | 57 | return rl 58 | } 59 | 60 | module.exports = runInteractiveScript 61 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "interactive-script", 3 | "version": "1.0.0", 4 | "description": "Easy to write interactive scripts", 5 | "main": "index.js", 6 | "keywords": [ 7 | "script", 8 | "interactive", 9 | "readline", 10 | "interface", 11 | "terminal" 12 | ], 13 | "author": "Lee Byron (http://leebyron.com/)", 14 | "license": "MIT" 15 | } 16 | --------------------------------------------------------------------------------