1# promzard 2 3A prompting wizard for building files from specialized PromZard modules. 4Used by `npm init`. 5 6A reimplementation of @SubStack's 7[prompter](https://github.com/substack/node-prompter), which does not 8use AST traversal. 9 10From another point of view, it's a reimplementation of 11[@Marak](https://github.com/marak)'s 12[wizard](https://github.com/Marak/wizard) which doesn't use schemas. 13 14The goal is a nice drop-in enhancement for `npm init`. 15 16## Usage 17 18```javascript 19var promzard = require('promzard') 20promzard(inputFile, optionalContextAdditions, function (er, data) { 21 // .. you know what you doing .. 22}) 23``` 24 25In the `inputFile` you can have something like this: 26 27```javascript 28var fs = require('fs') 29module.exports = { 30 "greeting": prompt("Who shall you greet?", "world", function (who) { 31 return "Hello, " + who 32 }), 33 "filename": __filename, 34 "directory": function (cb) { 35 fs.readdir(__dirname, cb) 36 } 37} 38``` 39 40When run, promzard will display the prompts and resolve the async 41functions in order, and then either give you an error, or the resolved 42data, ready to be dropped into a JSON file or some other place. 43 44 45### promzard(inputFile, ctx, callback) 46 47The inputFile is just a node module. You can require() things, set 48module.exports, etc. Whatever that module exports is the result, and it 49is walked over to call any functions as described below. 50 51The only caveat is that you must give PromZard the full absolute path 52to the module (you can get this via Node's `require.resolve`.) Also, 53the `prompt` function is injected into the context object, so watch out. 54 55Whatever you put in that `ctx` will of course also be available in the 56module. You can get quite fancy with this, passing in existing configs 57and so on. 58 59### Class: promzard.PromZard(file, ctx) 60 61Just like the `promzard` function, but the EventEmitter that makes it 62all happen. Emits either a `data` event with the data, or a `error` 63event if it blows up. 64 65If `error` is emitted, then `data` never will be. 66 67### prompt(...) 68 69In the promzard input module, you can call the `prompt` function. 70This prompts the user to input some data. The arguments are interpreted 71based on type: 72 731. `string` The first string encountered is the prompt. The second is 74 the default value. 752. `function` A transformer function which receives the data and returns 76 something else. More than meets the eye. 773. `object` The `prompt` member is the prompt, the `default` member is 78 the default value, and the `transform` is the transformer. 79 80Whatever the final value is, that's what will be put on the resulting 81object. 82 83### Functions 84 85If there are any functions on the promzard input module's exports, then 86promzard will call each of them with a callback. This way, your module 87can do asynchronous actions if necessary to validate or ascertain 88whatever needs verification. 89 90The functions are called in the context of the ctx object, and are given 91a single argument, which is a callback that should be called with either 92an error, or the result to assign to that spot. 93 94In the async function, you can also call prompt() and return the result 95of the prompt in the callback. 96 97For example, this works fine in a promzard module: 98 99``` 100exports.asyncPrompt = function (cb) { 101 fs.stat(someFile, function (er, st) { 102 // if there's an error, no prompt, just error 103 // otherwise prompt and use the actual file size as the default 104 cb(er, prompt('file size', st.size)) 105 }) 106} 107``` 108 109You can also return other async functions in the async function 110callback. Though that's a bit silly, it could be a handy way to reuse 111functionality in some cases. 112 113### Sync vs Async 114 115The `prompt()` function is not synchronous, though it appears that way. 116It just returns a token that is swapped out when the data object is 117walked over asynchronously later, and returns a token. 118 119For that reason, prompt() calls whose results don't end up on the data 120object are never shown to the user. For example, this will only prompt 121once: 122 123``` 124exports.promptThreeTimes = prompt('prompt me once', 'shame on you') 125exports.promptThreeTimes = prompt('prompt me twice', 'um....') 126exports.promptThreeTimes = prompt('you cant prompt me again') 127``` 128 129### Isn't this exactly the sort of 'looks sync' that you said was bad about other libraries? 130 131Yeah, sorta. I wouldn't use promzard for anything more complicated than 132a wizard that spits out prompts to set up a config file or something. 133Maybe there are other use cases I haven't considered. 134