cli-questionnaire 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +13 -0
- package/README.md +185 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +45 -0
- package/dist/prompt/handlers/conditionHandler.d.ts +2 -0
- package/dist/prompt/handlers/conditionHandler.js +6 -0
- package/dist/prompt/handlers/multipleChoiceHandler.d.ts +3 -0
- package/dist/prompt/handlers/multipleChoiceHandler.js +48 -0
- package/dist/prompt/handlers/numberHandler.d.ts +3 -0
- package/dist/prompt/handlers/numberHandler.js +40 -0
- package/dist/prompt/handlers/openHandler.d.ts +3 -0
- package/dist/prompt/handlers/openHandler.js +35 -0
- package/dist/prompt/helpers.d.ts +2 -0
- package/dist/prompt/helpers.js +7 -0
- package/dist/prompt/index.d.ts +8 -0
- package/dist/prompt/index.js +82 -0
- package/dist/types/index.d.ts +24 -0
- package/dist/types/index.js +2 -0
- package/package.json +43 -0
package/LICENSE
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright (c) 2025 Edmar Langendoen
|
2
|
+
|
3
|
+
Permission to use, copy, modify, and distribute this software for any
|
4
|
+
purpose with or without fee is hereby granted, provided that the above
|
5
|
+
copyright notice and this permission notice appear in all copies.
|
6
|
+
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
8
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
9
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
10
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
11
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
12
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
13
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
# CLI Questionnaire 🎯
|
2
|
+
|
3
|
+
A CLI tool built with TypeScript for creating interactive questionnaires.
|
4
|
+
|
5
|
+
## Table of Contents 📚
|
6
|
+
|
7
|
+
- [Installation](#installation)
|
8
|
+
- [Usage](#usage)
|
9
|
+
- [Build](#build)
|
10
|
+
- [Testing](#testing)
|
11
|
+
- [License](#license)
|
12
|
+
|
13
|
+
## Installation ⚙️
|
14
|
+
|
15
|
+
### From npm (once published)
|
16
|
+
|
17
|
+
You can install the package globally using npm:
|
18
|
+
|
19
|
+
```sh
|
20
|
+
npm install -g cli-questionnaire
|
21
|
+
```
|
22
|
+
|
23
|
+
Or use it directly with `npx`:
|
24
|
+
|
25
|
+
```sh
|
26
|
+
npx cli-questionnaire
|
27
|
+
```
|
28
|
+
|
29
|
+
### From Source 🛠️
|
30
|
+
|
31
|
+
1. Clone the repository:
|
32
|
+
```sh
|
33
|
+
git clone https://github.com/elangendoen/cli-questionnaire.git
|
34
|
+
cd cli-questionnaire
|
35
|
+
```
|
36
|
+
2. Install dependencies:
|
37
|
+
|
38
|
+
```sh
|
39
|
+
npm install
|
40
|
+
```
|
41
|
+
|
42
|
+
3. Build the project:
|
43
|
+
```sh
|
44
|
+
npm run build
|
45
|
+
```
|
46
|
+
|
47
|
+
## Usage 🚀
|
48
|
+
|
49
|
+
### Using the CLI
|
50
|
+
|
51
|
+
Once installed globally or via `npx`, you can run the CLI tool:
|
52
|
+
|
53
|
+
```sh
|
54
|
+
cli-questionnaire
|
55
|
+
```
|
56
|
+
|
57
|
+
### Programmatic Usage 🖥️
|
58
|
+
|
59
|
+
You can also use the `Prompt` function programmatically in your TypeScript or JavaScript projects:
|
60
|
+
|
61
|
+
```typescript
|
62
|
+
import { Prompt, Question } from 'cli-questionnaire';
|
63
|
+
|
64
|
+
const questions: Question[] = [
|
65
|
+
{
|
66
|
+
id: 'q1',
|
67
|
+
type: 'multiple-choice',
|
68
|
+
question: 'What is your favorite programming language?',
|
69
|
+
options: ['JavaScript', 'TypeScript', 'Python'],
|
70
|
+
},
|
71
|
+
{
|
72
|
+
id: 'q2',
|
73
|
+
type: 'open',
|
74
|
+
question: 'What is your name?',
|
75
|
+
allowBackNavigation: true,
|
76
|
+
},
|
77
|
+
{
|
78
|
+
id: 'q3',
|
79
|
+
type: 'number',
|
80
|
+
question: 'How many years of experience do you have in programming?',
|
81
|
+
condition: (answers) => {
|
82
|
+
const q1Answer = answers.find((a) => a.id === 'q1')?.answer;
|
83
|
+
return q1Answer === 'TypeScript';
|
84
|
+
},
|
85
|
+
},
|
86
|
+
];
|
87
|
+
|
88
|
+
(async () => {
|
89
|
+
const answers = await Prompt({
|
90
|
+
questions,
|
91
|
+
allowBackNavigation: true,
|
92
|
+
allowSkip: true,
|
93
|
+
});
|
94
|
+
|
95
|
+
console.log('Your answers:', answers);
|
96
|
+
})();
|
97
|
+
```
|
98
|
+
|
99
|
+
#### Question Properties 📝
|
100
|
+
|
101
|
+
Each question in the `questions` array can have the following properties:
|
102
|
+
|
103
|
+
- **`id`** (required): A unique identifier for the question.
|
104
|
+
- **`type`** (required): The type of question. Can be one of:
|
105
|
+
- `'multiple-choice'`: A question with predefined options.
|
106
|
+
- `'open'`: A free-text question.
|
107
|
+
- `'number'`: A question expecting a numeric answer.
|
108
|
+
- **`question`** (required): The text of the question to display to the user.
|
109
|
+
- **`options`** (required for `'multiple-choice'`): An array of strings representing the available options.
|
110
|
+
- **`allowBackNavigation`** (optional): A boolean indicating whether the user can navigate back to this question. Defaults to `false`.
|
111
|
+
- **`allowSkip`** (optional): A boolean indicating whether the user can skip this question. Defaults to `false`.
|
112
|
+
- **`condition`** (optional): A function that determines whether this question should be asked. The function receives the current `answers` array and should return `true` or `false`.
|
113
|
+
|
114
|
+
---
|
115
|
+
|
116
|
+
### Question Types with Examples 🛠️
|
117
|
+
|
118
|
+
Here are examples of the different question types supported by the `Prompt` function:
|
119
|
+
|
120
|
+
#### Multiple-Choice Question
|
121
|
+
|
122
|
+
A question with predefined options that the user can select from:
|
123
|
+
|
124
|
+
```typescript
|
125
|
+
{
|
126
|
+
id: 'q1',
|
127
|
+
type: 'multiple-choice',
|
128
|
+
question: 'What is your favorite programming language?',
|
129
|
+
options: ['JavaScript', 'TypeScript', 'Python', 'Java'],
|
130
|
+
}
|
131
|
+
```
|
132
|
+
|
133
|
+
#### Open Question
|
134
|
+
|
135
|
+
A free-text question where the user can type their answer:
|
136
|
+
|
137
|
+
```typescript
|
138
|
+
{
|
139
|
+
id: 'q1',
|
140
|
+
type: 'open',
|
141
|
+
question: 'What is your name?',
|
142
|
+
}
|
143
|
+
```
|
144
|
+
|
145
|
+
#### Number Question
|
146
|
+
|
147
|
+
A question expecting a numeric answer:
|
148
|
+
|
149
|
+
```typescript
|
150
|
+
{
|
151
|
+
id: 'q1',
|
152
|
+
type: 'number',
|
153
|
+
question: 'How many years of experience do you have in programming?',
|
154
|
+
}
|
155
|
+
```
|
156
|
+
|
157
|
+
---
|
158
|
+
|
159
|
+
## Build 🏗️
|
160
|
+
|
161
|
+
To compile the TypeScript code to JavaScript, run:
|
162
|
+
|
163
|
+
```sh
|
164
|
+
npm run build
|
165
|
+
```
|
166
|
+
|
167
|
+
The compiled files will be output to the `dist` directory.
|
168
|
+
|
169
|
+
## Testing 🧪
|
170
|
+
|
171
|
+
Run the tests using Jest:
|
172
|
+
|
173
|
+
```sh
|
174
|
+
npm test
|
175
|
+
```
|
176
|
+
|
177
|
+
To generate a coverage report:
|
178
|
+
|
179
|
+
```sh
|
180
|
+
npm run coverage
|
181
|
+
```
|
182
|
+
|
183
|
+
## License 📜
|
184
|
+
|
185
|
+
This project is licensed under the ISC License.
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.Prompt = void 0;
|
4
|
+
var prompt_1 = require("./prompt");
|
5
|
+
Object.defineProperty(exports, "Prompt", { enumerable: true, get: function () { return prompt_1.Prompt; } });
|
6
|
+
// import { Prompt } from './prompt';
|
7
|
+
// import { Question } from './types';
|
8
|
+
// (async () => {
|
9
|
+
// const questions: Question[] = [
|
10
|
+
// {
|
11
|
+
// id: 'q1',
|
12
|
+
// type: 'multiple-choice',
|
13
|
+
// question: 'What is your favorite programming language?',
|
14
|
+
// options: ['JavaScript', 'TypeScript', 'Python', 'Java'],
|
15
|
+
// },
|
16
|
+
// {
|
17
|
+
// id: 'q2',
|
18
|
+
// type: 'open',
|
19
|
+
// question: 'What is your name?',
|
20
|
+
// allowBackNavigation: true,
|
21
|
+
// },
|
22
|
+
// {
|
23
|
+
// id: 'q3',
|
24
|
+
// type: 'number',
|
25
|
+
// question: 'How many years of experience do you have in programming?',
|
26
|
+
// condition: (answers) => {
|
27
|
+
// // Only ask this question if the user selected "TypeScript" in q1
|
28
|
+
// const q1Answer = answers.find((a) => a.id === 'q1')?.answer;
|
29
|
+
// return q1Answer === 'TypeScript';
|
30
|
+
// },
|
31
|
+
// },
|
32
|
+
// {
|
33
|
+
// id: 'q4',
|
34
|
+
// type: 'open',
|
35
|
+
// question: 'What is your last name?',
|
36
|
+
// allowBackNavigation: true,
|
37
|
+
// },
|
38
|
+
// ];
|
39
|
+
// const answers = await Prompt({
|
40
|
+
// questions,
|
41
|
+
// allowBackNavigation: true,
|
42
|
+
// allowSkip: true,
|
43
|
+
// });
|
44
|
+
// console.log('\nYour answers:', answers);
|
45
|
+
// })();
|
@@ -0,0 +1,3 @@
|
|
1
|
+
import { Answer, MultipleChoiceQuestion } from '../../types';
|
2
|
+
import * as readline from 'readline';
|
3
|
+
export declare function handleMultipleChoice(question: MultipleChoiceQuestion, answers: Answer[], rl: readline.Interface, allowBackNavigation: boolean, allowSkip: boolean, currentIndex: number): Promise<string | number | 'back' | 'skip'>;
|
@@ -0,0 +1,48 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
9
|
+
});
|
10
|
+
};
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
12
|
+
exports.handleMultipleChoice = handleMultipleChoice;
|
13
|
+
const helpers_1 = require("../helpers");
|
14
|
+
function handleMultipleChoice(question, answers, rl, allowBackNavigation, allowSkip, currentIndex) {
|
15
|
+
return __awaiter(this, void 0, void 0, function* () {
|
16
|
+
console.log(`\n${question.question}`);
|
17
|
+
question.options.forEach((option, index) => {
|
18
|
+
console.log(`${index + 1}. ${option}`);
|
19
|
+
});
|
20
|
+
// Add "Go Back" and "Skip" options if applicable
|
21
|
+
const extraOptions = [];
|
22
|
+
if (allowBackNavigation && currentIndex > 0) {
|
23
|
+
extraOptions.push('b. Go Back');
|
24
|
+
}
|
25
|
+
if (allowSkip) {
|
26
|
+
extraOptions.push('s. Skip');
|
27
|
+
}
|
28
|
+
if (extraOptions.length > 0) {
|
29
|
+
console.log('\nAdditional Options:');
|
30
|
+
extraOptions.forEach((option) => {
|
31
|
+
console.log(option);
|
32
|
+
});
|
33
|
+
}
|
34
|
+
const choice = yield (0, helpers_1.askQuestion)(rl, 'Choose an option (number or letter): ');
|
35
|
+
const index = parseInt(choice, 10) - 1;
|
36
|
+
if (allowBackNavigation && currentIndex > 0 && choice.toLowerCase() === 'b') {
|
37
|
+
return 'back';
|
38
|
+
}
|
39
|
+
if (allowSkip && choice.toLowerCase() === 's') {
|
40
|
+
return 'skip';
|
41
|
+
}
|
42
|
+
if (index >= 0 && index < question.options.length) {
|
43
|
+
return question.options[index];
|
44
|
+
}
|
45
|
+
console.log('Invalid choice. Please try again.');
|
46
|
+
return yield handleMultipleChoice(question, answers, rl, allowBackNavigation, allowSkip, currentIndex);
|
47
|
+
});
|
48
|
+
}
|
@@ -0,0 +1,40 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
9
|
+
});
|
10
|
+
};
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
12
|
+
exports.handleNumber = handleNumber;
|
13
|
+
const helpers_1 = require("../helpers");
|
14
|
+
function handleNumber(question, rl, allowBackNavigation, allowSkip, currentIndex) {
|
15
|
+
return __awaiter(this, void 0, void 0, function* () {
|
16
|
+
let extraOptions = '';
|
17
|
+
if (allowBackNavigation && currentIndex > 0) {
|
18
|
+
extraOptions += ' (b: Go Back';
|
19
|
+
}
|
20
|
+
if (allowSkip) {
|
21
|
+
extraOptions += `${extraOptions ? ', ' : ' ('}s: Skip`;
|
22
|
+
}
|
23
|
+
extraOptions += extraOptions ? ')' : '';
|
24
|
+
const response = yield (0, helpers_1.askQuestion)(rl, `${question.question}${extraOptions}: `);
|
25
|
+
if (allowBackNavigation &&
|
26
|
+
currentIndex > 0 &&
|
27
|
+
response.toLowerCase() === 'b') {
|
28
|
+
return 'back';
|
29
|
+
}
|
30
|
+
if (allowSkip && response.toLowerCase() === 's') {
|
31
|
+
return 'skip';
|
32
|
+
}
|
33
|
+
const numberResponse = parseFloat(response);
|
34
|
+
if (!isNaN(numberResponse)) {
|
35
|
+
return numberResponse;
|
36
|
+
}
|
37
|
+
console.log('Invalid number. Please try again.');
|
38
|
+
return yield handleNumber(question, rl, allowBackNavigation, allowSkip, currentIndex);
|
39
|
+
});
|
40
|
+
}
|
@@ -0,0 +1,35 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
9
|
+
});
|
10
|
+
};
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
12
|
+
exports.handleOpen = handleOpen;
|
13
|
+
const helpers_1 = require("../helpers");
|
14
|
+
function handleOpen(question, rl, allowBackNavigation, allowSkip, currentIndex) {
|
15
|
+
return __awaiter(this, void 0, void 0, function* () {
|
16
|
+
let extraOptions = '';
|
17
|
+
if (allowBackNavigation && currentIndex > 0) {
|
18
|
+
extraOptions += ' (b: Go Back';
|
19
|
+
}
|
20
|
+
if (allowSkip) {
|
21
|
+
extraOptions += `${extraOptions ? ', ' : ' ('}s: Skip`;
|
22
|
+
}
|
23
|
+
extraOptions += extraOptions ? ')' : '';
|
24
|
+
const response = yield (0, helpers_1.askQuestion)(rl, `${question.question}${extraOptions}: `);
|
25
|
+
if (allowBackNavigation &&
|
26
|
+
currentIndex > 0 &&
|
27
|
+
response.toLowerCase() === 'b') {
|
28
|
+
return 'back';
|
29
|
+
}
|
30
|
+
if (allowSkip && response.toLowerCase() === 's') {
|
31
|
+
return 'skip';
|
32
|
+
}
|
33
|
+
return response;
|
34
|
+
});
|
35
|
+
}
|
@@ -0,0 +1,8 @@
|
|
1
|
+
import { Question, Answer } from '../types';
|
2
|
+
interface PromptParams {
|
3
|
+
questions: Question[];
|
4
|
+
allowBackNavigation?: boolean;
|
5
|
+
allowSkip?: boolean;
|
6
|
+
}
|
7
|
+
export declare function Prompt({ questions, allowBackNavigation, allowSkip, }: PromptParams): Promise<Answer[]>;
|
8
|
+
export {};
|
@@ -0,0 +1,82 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
9
|
+
});
|
10
|
+
};
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
12
|
+
exports.Prompt = Prompt;
|
13
|
+
const readline = require("readline");
|
14
|
+
const multipleChoiceHandler_1 = require("./handlers/multipleChoiceHandler");
|
15
|
+
const openHandler_1 = require("./handlers/openHandler");
|
16
|
+
const numberHandler_1 = require("./handlers/numberHandler");
|
17
|
+
const conditionHandler_1 = require("./handlers/conditionHandler");
|
18
|
+
function Prompt(_a) {
|
19
|
+
return __awaiter(this, arguments, void 0, function* ({ questions, allowBackNavigation = false, allowSkip = false, }) {
|
20
|
+
var _b, _c;
|
21
|
+
const answers = [];
|
22
|
+
const rl = readline.createInterface({
|
23
|
+
input: process.stdin,
|
24
|
+
output: process.stdout,
|
25
|
+
});
|
26
|
+
let currentIndex = 0;
|
27
|
+
try {
|
28
|
+
while (currentIndex < questions.length) {
|
29
|
+
const question = questions[currentIndex];
|
30
|
+
// Check the condition function, if it exists
|
31
|
+
if (!(0, conditionHandler_1.handleCondition)(question, [...answers])) {
|
32
|
+
// Skip the question if the condition returns false
|
33
|
+
answers.push({ id: question.id, answer: undefined });
|
34
|
+
currentIndex++;
|
35
|
+
continue;
|
36
|
+
}
|
37
|
+
const backNavigationAllowed = (_b = question.allowBackNavigation) !== null && _b !== void 0 ? _b : allowBackNavigation;
|
38
|
+
const skipAllowed = (_c = question.allowSkip) !== null && _c !== void 0 ? _c : allowSkip;
|
39
|
+
let userAnswer = undefined;
|
40
|
+
// Delegate to the appropriate handler based on question type
|
41
|
+
switch (question.type) {
|
42
|
+
case 'multiple-choice':
|
43
|
+
userAnswer = yield (0, multipleChoiceHandler_1.handleMultipleChoice)(question, [...answers], // Pass a copy of the current answers
|
44
|
+
rl, backNavigationAllowed, skipAllowed, currentIndex);
|
45
|
+
break;
|
46
|
+
case 'open':
|
47
|
+
userAnswer = yield (0, openHandler_1.handleOpen)(question, rl, backNavigationAllowed, skipAllowed, currentIndex);
|
48
|
+
break;
|
49
|
+
case 'number':
|
50
|
+
userAnswer = yield (0, numberHandler_1.handleNumber)(question, rl, backNavigationAllowed, skipAllowed, currentIndex);
|
51
|
+
break;
|
52
|
+
default:
|
53
|
+
console.log('Unknown question type. Skipping question.');
|
54
|
+
userAnswer = undefined;
|
55
|
+
}
|
56
|
+
// Handle navigation
|
57
|
+
if (userAnswer === 'back') {
|
58
|
+
if (currentIndex > 0) {
|
59
|
+
currentIndex--;
|
60
|
+
answers.pop(); // Remove the last answer
|
61
|
+
}
|
62
|
+
else {
|
63
|
+
console.log('You are already at the first question. Cannot go back.');
|
64
|
+
}
|
65
|
+
continue;
|
66
|
+
}
|
67
|
+
else if (userAnswer === 'skip') {
|
68
|
+
answers.push({ id: question.id, answer: undefined });
|
69
|
+
}
|
70
|
+
else {
|
71
|
+
answers.push({ id: question.id, answer: userAnswer });
|
72
|
+
}
|
73
|
+
currentIndex++;
|
74
|
+
}
|
75
|
+
}
|
76
|
+
finally {
|
77
|
+
// Ensure the readline interface is always closed
|
78
|
+
rl.close();
|
79
|
+
}
|
80
|
+
return answers;
|
81
|
+
});
|
82
|
+
}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
export type QuestionType = 'multiple-choice' | 'open' | 'number';
|
2
|
+
export interface MultipleChoiceQuestion {
|
3
|
+
id: string;
|
4
|
+
type: 'multiple-choice';
|
5
|
+
question: string;
|
6
|
+
options: string[];
|
7
|
+
allowBackNavigation?: boolean;
|
8
|
+
allowSkip?: boolean;
|
9
|
+
condition?: (answers: Answer[]) => boolean;
|
10
|
+
}
|
11
|
+
export interface OpenOrNumberQuestion {
|
12
|
+
id: string;
|
13
|
+
type: 'open' | 'number';
|
14
|
+
question: string;
|
15
|
+
options?: undefined;
|
16
|
+
allowBackNavigation?: boolean;
|
17
|
+
allowSkip?: boolean;
|
18
|
+
condition?: (answers: Answer[]) => boolean;
|
19
|
+
}
|
20
|
+
export type Question = MultipleChoiceQuestion | OpenOrNumberQuestion;
|
21
|
+
export type Answer = {
|
22
|
+
id: string;
|
23
|
+
answer: string | number | undefined;
|
24
|
+
};
|
package/package.json
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
{
|
2
|
+
"name": "cli-questionnaire",
|
3
|
+
"version": "1.0.0",
|
4
|
+
"main": "dist/index.js",
|
5
|
+
"types": "dist/index.d.ts",
|
6
|
+
"bin": {
|
7
|
+
"cli-questionnaire": "./dist/index.js"
|
8
|
+
},
|
9
|
+
"scripts": {
|
10
|
+
"build": "tsc",
|
11
|
+
"prepare": "npm run build",
|
12
|
+
"start": "ts-node src/index.ts",
|
13
|
+
"test": "jest --forceExit",
|
14
|
+
"coverage": "jest --coverage"
|
15
|
+
},
|
16
|
+
"files": [
|
17
|
+
"dist",
|
18
|
+
"README.md"
|
19
|
+
],
|
20
|
+
"repository": {
|
21
|
+
"type": "git",
|
22
|
+
"url": "https://github.com/elangendoen/cli-questionnaire.git"
|
23
|
+
},
|
24
|
+
"keywords": ["cli", "questionnaire", "interactive", "prompts"],
|
25
|
+
"author": "Edmar Langendoen",
|
26
|
+
"license": "ISC",
|
27
|
+
"description": "A CLI questionnaire tool for interactive prompts.",
|
28
|
+
"devDependencies": {
|
29
|
+
"@eslint/js": "^9.26.0",
|
30
|
+
"@types/jest": "^29.5.14",
|
31
|
+
"@types/node": "^22.15.3",
|
32
|
+
"eslint": "^9.26.0",
|
33
|
+
"eslint-config-prettier": "^10.1.2",
|
34
|
+
"eslint-plugin-prettier": "^5.3.1",
|
35
|
+
"globals": "^16.0.0",
|
36
|
+
"jest": "^29.7.0",
|
37
|
+
"prettier": "^3.5.3",
|
38
|
+
"ts-jest": "^29.3.2",
|
39
|
+
"ts-node": "^10.9.2",
|
40
|
+
"typescript": "^5.8.3",
|
41
|
+
"typescript-eslint": "^8.31.1"
|
42
|
+
}
|
43
|
+
}
|