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