mepcli 0.1.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 CodeTease
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # Mep: Minimalist CLI Prompt
2
+
3
+ **Mep** is a minimalist and zero-dependency library for creating interactive command-line prompts in Node.js. It focuses on simplicity, modern design, and robust input handling, including support for cursor movement and input validation.
4
+
5
+ ## Features
6
+
7
+ - **Zero Dependency:** Keeps your project clean and fast.
8
+ - **Full-Featured Prompts:** Includes `text`, `password`, `select`, ch`eckbox, and `confirm`.
9
+ - **Responsive Input:** Supports cursor movement (Left/Right) and character insertion/deletion in `text` and `password` prompts.
10
+ - **Validation:** Built-in support for input validation with custom error messages.
11
+ - **Elegant Look:** Uses ANSI colors for a clean, modern CLI experience.
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install mepcli
17
+ # or
18
+ yarn add mepcli
19
+ ```
20
+
21
+ ## Usage Example
22
+
23
+ Mep provides a static class facade, `MepCLI`, for all interactions.
24
+ ```javascript
25
+ import { MepCLI } from 'mepcli';
26
+
27
+ async function setup() {
28
+ // Text input with validation and cursor support
29
+ const projectName = await MepCLI.text({
30
+ message: "Enter the project name:",
31
+ validate: (v) => v.length > 5 || "Must be longer than 5 chars",
32
+ });
33
+
34
+ // Select menu
35
+ const choice = await MepCLI.select({
36
+ message: "Choose an option:",
37
+ choices: [
38
+ { title: "Option A", value: 1 },
39
+ { title: "Option B", value: 2 }
40
+ ]
41
+ });
42
+
43
+ console.log(`\nProject: ${projectName}, Selected: ${choice}`);
44
+ }
45
+
46
+ setup();
47
+ ```
48
+
49
+ ## License
50
+
51
+ This project is under the **MIT License**.
package/dist/ansi.d.ts ADDED
@@ -0,0 +1,22 @@
1
+ /**
2
+ * ANSI Escape Codes
3
+ * Manual definitions to maintain zero-dependency status.
4
+ */
5
+ export declare const ANSI: {
6
+ RESET: string;
7
+ BOLD: string;
8
+ DIM: string;
9
+ ITALIC: string;
10
+ FG_GREEN: string;
11
+ FG_CYAN: string;
12
+ FG_YELLOW: string;
13
+ FG_RED: string;
14
+ FG_GRAY: string;
15
+ FG_WHITE: string;
16
+ ERASE_LINE: string;
17
+ CURSOR_LEFT: string;
18
+ HIDE_CURSOR: string;
19
+ SHOW_CURSOR: string;
20
+ UP: string;
21
+ DOWN: string;
22
+ };
package/dist/ansi.js ADDED
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ /**
3
+ * ANSI Escape Codes
4
+ * Manual definitions to maintain zero-dependency status.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.ANSI = void 0;
8
+ exports.ANSI = {
9
+ RESET: '\x1b[0m',
10
+ BOLD: '\x1b[1m',
11
+ DIM: '\x1b[2m',
12
+ ITALIC: '\x1b[3m',
13
+ // Colors
14
+ FG_GREEN: '\x1b[32m',
15
+ FG_CYAN: '\x1b[36m',
16
+ FG_YELLOW: '\x1b[33m',
17
+ FG_RED: '\x1b[31m',
18
+ FG_GRAY: '\x1b[90m',
19
+ FG_WHITE: '\x1b[37m',
20
+ // Cursor & Erasing
21
+ ERASE_LINE: '\x1b[2K', // Clear current line
22
+ CURSOR_LEFT: '\x1b[1000D', // Move cursor to start of line
23
+ HIDE_CURSOR: '\x1b[?25l',
24
+ SHOW_CURSOR: '\x1b[?25h',
25
+ UP: '\x1b[A',
26
+ DOWN: '\x1b[B',
27
+ };
package/dist/core.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ import { TextOptions, SelectOptions, ConfirmOptions, CheckboxOptions } from './types';
2
+ /**
3
+ * Public Facade for MepCLI
4
+ */
5
+ export declare class MepCLI {
6
+ static text(options: TextOptions): Promise<string>;
7
+ static select(options: SelectOptions): Promise<any>;
8
+ static checkbox(options: CheckboxOptions): Promise<any[]>;
9
+ static confirm(options: ConfirmOptions): Promise<boolean>;
10
+ static password(options: TextOptions): Promise<string>;
11
+ }
package/dist/core.js ADDED
@@ -0,0 +1,366 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MepCLI = void 0;
4
+ const ansi_1 = require("./ansi");
5
+ /**
6
+ * Abstract base class for all prompts.
7
+ * Handles common logic like stdin management, raw mode, and cleanup
8
+ * to enforce DRY (Don't Repeat Yourself) principles.
9
+ */
10
+ class Prompt {
11
+ constructor(options) {
12
+ this.options = options;
13
+ this.stdin = process.stdin;
14
+ this.stdout = process.stdout;
15
+ }
16
+ print(text) {
17
+ this.stdout.write(text);
18
+ }
19
+ /**
20
+ * Starts the prompt interaction.
21
+ * Sets up raw mode and listeners, returning a Promise.
22
+ */
23
+ run() {
24
+ return new Promise((resolve, reject) => {
25
+ this._resolve = resolve;
26
+ this._reject = reject;
27
+ if (typeof this.stdin.setRawMode === 'function') {
28
+ this.stdin.setRawMode(true);
29
+ }
30
+ this.stdin.resume();
31
+ this.stdin.setEncoding('utf8');
32
+ // Initial render: Default to hidden cursor (good for menus)
33
+ // Subclasses like TextPrompt will explicitly show it if needed.
34
+ this.print(ansi_1.ANSI.HIDE_CURSOR);
35
+ this.render(true);
36
+ this._onDataHandler = (buffer) => {
37
+ const char = buffer.toString();
38
+ // Global Exit Handler (Ctrl+C)
39
+ if (char === '\u0003') {
40
+ this.cleanup();
41
+ this.print(ansi_1.ANSI.SHOW_CURSOR + '\n');
42
+ if (this._reject)
43
+ this._reject(new Error('User force closed'));
44
+ return;
45
+ }
46
+ this.handleInput(char, buffer);
47
+ };
48
+ this.stdin.on('data', this._onDataHandler);
49
+ });
50
+ }
51
+ /**
52
+ * Cleans up listeners and restores stdin state.
53
+ */
54
+ cleanup() {
55
+ if (this._onDataHandler) {
56
+ this.stdin.removeListener('data', this._onDataHandler);
57
+ }
58
+ if (typeof this.stdin.setRawMode === 'function') {
59
+ this.stdin.setRawMode(false);
60
+ }
61
+ this.stdin.pause();
62
+ this.print(ansi_1.ANSI.SHOW_CURSOR);
63
+ }
64
+ /**
65
+ * Submits the final value and resolves the promise.
66
+ */
67
+ submit(result) {
68
+ this.cleanup();
69
+ this.print('\n');
70
+ if (this._resolve)
71
+ this._resolve(result);
72
+ }
73
+ }
74
+ // --- Implementation: Text Prompt ---
75
+ class TextPrompt extends Prompt {
76
+ constructor(options) {
77
+ super(options);
78
+ this.errorMsg = '';
79
+ this.cursor = 0;
80
+ this.value = options.initial || '';
81
+ this.cursor = this.value.length;
82
+ }
83
+ render(firstRender) {
84
+ // TextPrompt needs the cursor visible!
85
+ this.print(ansi_1.ANSI.SHOW_CURSOR);
86
+ if (!firstRender) {
87
+ this.print(ansi_1.ANSI.ERASE_LINE + ansi_1.ANSI.CURSOR_LEFT);
88
+ if (this.errorMsg) {
89
+ this.print(ansi_1.ANSI.UP + ansi_1.ANSI.ERASE_LINE + ansi_1.ANSI.CURSOR_LEFT);
90
+ }
91
+ }
92
+ // 1. Render the Prompt Message
93
+ this.print(ansi_1.ANSI.ERASE_LINE + ansi_1.ANSI.CURSOR_LEFT);
94
+ const icon = this.errorMsg ? `${ansi_1.ANSI.FG_RED}✖` : `${ansi_1.ANSI.FG_GREEN}?`;
95
+ this.print(`${icon} ${ansi_1.ANSI.BOLD}${this.options.message}${ansi_1.ANSI.RESET} `);
96
+ // 2. Render the Value or Placeholder
97
+ if (!this.value && this.options.placeholder && !this.errorMsg) {
98
+ this.print(`${ansi_1.ANSI.FG_GRAY}${this.options.placeholder}${ansi_1.ANSI.RESET}`);
99
+ // Move cursor back to start so typing overwrites placeholder visually
100
+ this.print(`\x1b[${this.options.placeholder.length}D`);
101
+ }
102
+ else {
103
+ const displayValue = this.options.isPassword ? '*'.repeat(this.value.length) : this.value;
104
+ this.print(`${ansi_1.ANSI.FG_CYAN}${displayValue}${ansi_1.ANSI.RESET}`);
105
+ }
106
+ // 3. Handle Error Message
107
+ if (this.errorMsg) {
108
+ this.print(`\n${ansi_1.ANSI.ERASE_LINE}${ansi_1.ANSI.FG_RED}>> ${this.errorMsg}${ansi_1.ANSI.RESET}`);
109
+ this.print(ansi_1.ANSI.UP); // Go back to input line
110
+ // Re-calculate position to end of input
111
+ const promptLen = this.options.message.length + 3; // Icon + 2 spaces
112
+ const valLen = this.value.length;
113
+ // Move to absolute start of line, then move right to end of string
114
+ this.print(`\x1b[1000D\x1b[${promptLen + valLen}C`);
115
+ }
116
+ // 4. Position Cursor Logic
117
+ // At this point, the physical cursor is at the END of the value string.
118
+ // We need to move it LEFT by (length - cursor_index)
119
+ const diff = this.value.length - this.cursor;
120
+ if (diff > 0) {
121
+ this.print(`\x1b[${diff}D`);
122
+ }
123
+ }
124
+ handleInput(char) {
125
+ // Enter
126
+ if (char === '\r' || char === '\n') {
127
+ if (this.options.validate) {
128
+ const validationResult = this.options.validate(this.value);
129
+ if (typeof validationResult === 'string' && validationResult.length > 0) {
130
+ this.errorMsg = validationResult;
131
+ this.render(false);
132
+ return;
133
+ }
134
+ }
135
+ if (this.errorMsg) {
136
+ this.print(`\n${ansi_1.ANSI.ERASE_LINE}${ansi_1.ANSI.UP}`);
137
+ }
138
+ this.submit(this.value);
139
+ return;
140
+ }
141
+ // Backspace
142
+ if (char === '\u0008' || char === '\x7f') {
143
+ if (this.cursor > 0) {
144
+ this.value = this.value.slice(0, this.cursor - 1) + this.value.slice(this.cursor);
145
+ this.cursor--;
146
+ this.errorMsg = '';
147
+ this.render(false);
148
+ }
149
+ return;
150
+ }
151
+ // Arrow Left
152
+ if (char === '\u001b[D') {
153
+ if (this.cursor > 0) {
154
+ this.cursor--;
155
+ this.render(false);
156
+ }
157
+ return;
158
+ }
159
+ // Arrow Right
160
+ if (char === '\u001b[C') {
161
+ if (this.cursor < this.value.length) {
162
+ this.cursor++;
163
+ this.render(false);
164
+ }
165
+ return;
166
+ }
167
+ // Delete key
168
+ if (char === '\u001b[3~') {
169
+ if (this.cursor < this.value.length) {
170
+ this.value = this.value.slice(0, this.cursor) + this.value.slice(this.cursor + 1);
171
+ this.errorMsg = '';
172
+ this.render(false);
173
+ }
174
+ return;
175
+ }
176
+ // Regular Typing
177
+ if (char.length === 1 && !/^[\x00-\x1F]/.test(char)) {
178
+ this.value = this.value.slice(0, this.cursor) + char + this.value.slice(this.cursor);
179
+ this.cursor++;
180
+ this.errorMsg = '';
181
+ this.render(false);
182
+ }
183
+ }
184
+ }
185
+ // --- Implementation: Select Prompt ---
186
+ class SelectPrompt extends Prompt {
187
+ constructor(options) {
188
+ super(options);
189
+ this.selectedIndex = 0;
190
+ }
191
+ render(firstRender) {
192
+ // Ensure cursor is HIDDEN for menus
193
+ this.print(ansi_1.ANSI.HIDE_CURSOR);
194
+ if (!firstRender) {
195
+ this.print(`\x1b[${this.options.choices.length + 1}A`);
196
+ }
197
+ this.print(`${ansi_1.ANSI.ERASE_LINE}${ansi_1.ANSI.CURSOR_LEFT}`);
198
+ this.print(`${ansi_1.ANSI.FG_GREEN}?${ansi_1.ANSI.RESET} ${ansi_1.ANSI.BOLD}${this.options.message}${ansi_1.ANSI.RESET}\n`);
199
+ this.options.choices.forEach((choice, index) => {
200
+ this.print(`${ansi_1.ANSI.ERASE_LINE}${ansi_1.ANSI.CURSOR_LEFT}`);
201
+ if (index === this.selectedIndex) {
202
+ this.print(`${ansi_1.ANSI.FG_CYAN}❯ ${choice.title}${ansi_1.ANSI.RESET}\n`);
203
+ }
204
+ else {
205
+ this.print(` ${choice.title}\n`);
206
+ }
207
+ });
208
+ }
209
+ handleInput(char) {
210
+ if (char === '\r' || char === '\n') {
211
+ this.cleanup();
212
+ this.print(`\x1b[${this.options.choices.length - this.selectedIndex}B`);
213
+ this.print(ansi_1.ANSI.SHOW_CURSOR);
214
+ if (this._resolve)
215
+ this._resolve(this.options.choices[this.selectedIndex].value);
216
+ return;
217
+ }
218
+ if (char === '\u001b[A') { // Up
219
+ this.selectedIndex = this.selectedIndex > 0 ? this.selectedIndex - 1 : this.options.choices.length - 1;
220
+ this.render(false);
221
+ }
222
+ if (char === '\u001b[B') { // Down
223
+ this.selectedIndex = this.selectedIndex < this.options.choices.length - 1 ? this.selectedIndex + 1 : 0;
224
+ this.render(false);
225
+ }
226
+ }
227
+ }
228
+ // --- Implementation: Checkbox Prompt ---
229
+ class CheckboxPrompt extends Prompt {
230
+ constructor(options) {
231
+ super(options);
232
+ this.selectedIndex = 0;
233
+ this.errorMsg = '';
234
+ this.checkedState = options.choices.map(c => !!c.selected);
235
+ }
236
+ render(firstRender) {
237
+ // Ensure cursor is HIDDEN for menus
238
+ this.print(ansi_1.ANSI.HIDE_CURSOR);
239
+ if (!firstRender) {
240
+ const extraLines = this.errorMsg ? 1 : 0;
241
+ this.print(`\x1b[${this.options.choices.length + 1 + extraLines}A`);
242
+ }
243
+ this.print(`${ansi_1.ANSI.ERASE_LINE}${ansi_1.ANSI.CURSOR_LEFT}`);
244
+ const icon = this.errorMsg ? `${ansi_1.ANSI.FG_RED}✖` : `${ansi_1.ANSI.FG_GREEN}?`;
245
+ this.print(`${icon} ${ansi_1.ANSI.BOLD}${this.options.message}${ansi_1.ANSI.RESET} ${ansi_1.ANSI.FG_GRAY}(Press <space> to select, <enter> to confirm)${ansi_1.ANSI.RESET}\n`);
246
+ this.options.choices.forEach((choice, index) => {
247
+ this.print(`${ansi_1.ANSI.ERASE_LINE}${ansi_1.ANSI.CURSOR_LEFT}`);
248
+ const cursor = index === this.selectedIndex ? `${ansi_1.ANSI.FG_CYAN}❯${ansi_1.ANSI.RESET}` : ' ';
249
+ const isChecked = this.checkedState[index];
250
+ const checkbox = isChecked
251
+ ? `${ansi_1.ANSI.FG_GREEN}◉${ansi_1.ANSI.RESET}`
252
+ : `${ansi_1.ANSI.FG_GRAY}◯${ansi_1.ANSI.RESET}`;
253
+ const title = index === this.selectedIndex
254
+ ? `${ansi_1.ANSI.FG_CYAN}${choice.title}${ansi_1.ANSI.RESET}`
255
+ : choice.title;
256
+ this.print(`${cursor} ${checkbox} ${title}\n`);
257
+ });
258
+ if (this.errorMsg) {
259
+ this.print(`${ansi_1.ANSI.ERASE_LINE}${ansi_1.ANSI.FG_RED}>> ${this.errorMsg}${ansi_1.ANSI.RESET}`);
260
+ }
261
+ else if (!firstRender) {
262
+ this.print(`${ansi_1.ANSI.ERASE_LINE}`);
263
+ }
264
+ }
265
+ handleInput(char) {
266
+ if (char === '\r' || char === '\n') {
267
+ const selectedCount = this.checkedState.filter(Boolean).length;
268
+ const { min = 0, max } = this.options;
269
+ if (selectedCount < min) {
270
+ this.errorMsg = `You must select at least ${min} options.`;
271
+ this.render(false);
272
+ return;
273
+ }
274
+ if (max && selectedCount > max) {
275
+ this.errorMsg = `You can only select up to ${max} options.`;
276
+ this.render(false);
277
+ return;
278
+ }
279
+ this.cleanup();
280
+ this.print(ansi_1.ANSI.SHOW_CURSOR + '\n');
281
+ const results = this.options.choices
282
+ .filter((_, i) => this.checkedState[i])
283
+ .map(c => c.value);
284
+ if (this._resolve)
285
+ this._resolve(results);
286
+ return;
287
+ }
288
+ if (char === ' ') {
289
+ const currentChecked = this.checkedState[this.selectedIndex];
290
+ const selectedCount = this.checkedState.filter(Boolean).length;
291
+ const { max } = this.options;
292
+ if (!currentChecked && max && selectedCount >= max) {
293
+ this.errorMsg = `Max ${max} selections allowed.`;
294
+ }
295
+ else {
296
+ this.checkedState[this.selectedIndex] = !currentChecked;
297
+ this.errorMsg = '';
298
+ }
299
+ this.render(false);
300
+ }
301
+ if (char === '\u001b[A') { // Up
302
+ this.selectedIndex = this.selectedIndex > 0 ? this.selectedIndex - 1 : this.options.choices.length - 1;
303
+ this.errorMsg = '';
304
+ this.render(false);
305
+ }
306
+ if (char === '\u001b[B') { // Down
307
+ this.selectedIndex = this.selectedIndex < this.options.choices.length - 1 ? this.selectedIndex + 1 : 0;
308
+ this.errorMsg = '';
309
+ this.render(false);
310
+ }
311
+ }
312
+ }
313
+ // --- Implementation: Confirm Prompt ---
314
+ class ConfirmPrompt extends Prompt {
315
+ constructor(options) {
316
+ super(options);
317
+ this.value = options.initial ?? true;
318
+ }
319
+ render(firstRender) {
320
+ // Hide cursor for confirm, user just hits Y/N or Enter
321
+ this.print(ansi_1.ANSI.HIDE_CURSOR);
322
+ if (!firstRender) {
323
+ this.print(`${ansi_1.ANSI.ERASE_LINE}${ansi_1.ANSI.CURSOR_LEFT}`);
324
+ }
325
+ const hint = this.value ? `${ansi_1.ANSI.BOLD}Yes${ansi_1.ANSI.RESET}/no` : `yes/${ansi_1.ANSI.BOLD}No${ansi_1.ANSI.RESET}`;
326
+ this.print(`${ansi_1.ANSI.FG_GREEN}?${ansi_1.ANSI.RESET} ${ansi_1.ANSI.BOLD}${this.options.message}${ansi_1.ANSI.RESET} ${ansi_1.ANSI.FG_GRAY}(${hint})${ansi_1.ANSI.RESET} `);
327
+ const text = this.value ? 'Yes' : 'No';
328
+ this.print(`${ansi_1.ANSI.FG_CYAN}${text}${ansi_1.ANSI.RESET}\x1b[${text.length}D`);
329
+ }
330
+ handleInput(char) {
331
+ const c = char.toLowerCase();
332
+ if (c === '\r' || c === '\n') {
333
+ this.submit(this.value);
334
+ return;
335
+ }
336
+ if (c === 'y') {
337
+ this.value = true;
338
+ this.render(false);
339
+ }
340
+ if (c === 'n') {
341
+ this.value = false;
342
+ this.render(false);
343
+ }
344
+ }
345
+ }
346
+ /**
347
+ * Public Facade for MepCLI
348
+ */
349
+ class MepCLI {
350
+ static text(options) {
351
+ return new TextPrompt(options).run();
352
+ }
353
+ static select(options) {
354
+ return new SelectPrompt(options).run();
355
+ }
356
+ static checkbox(options) {
357
+ return new CheckboxPrompt(options).run();
358
+ }
359
+ static confirm(options) {
360
+ return new ConfirmPrompt(options).run();
361
+ }
362
+ static password(options) {
363
+ return new TextPrompt({ ...options, isPassword: true }).run();
364
+ }
365
+ }
366
+ exports.MepCLI = MepCLI;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Mep (Minimalist Interactive CLI Prompt)
3
+ * Export file
4
+ */
5
+ export * from './types';
6
+ export * from './core';
package/dist/index.js ADDED
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ /**
3
+ * Mep (Minimalist Interactive CLI Prompt)
4
+ * Export file
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
18
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
19
+ };
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ __exportStar(require("./types"), exports);
22
+ __exportStar(require("./core"), exports);
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Type definitions for Mep CLI interactions.
3
+ */
4
+ export interface BaseOptions {
5
+ message: string;
6
+ }
7
+ export interface TextOptions extends BaseOptions {
8
+ placeholder?: string;
9
+ initial?: string;
10
+ validate?: (value: string) => string | boolean;
11
+ isPassword?: boolean;
12
+ }
13
+ export interface SelectChoice {
14
+ title: string;
15
+ value: any;
16
+ description?: string;
17
+ }
18
+ export interface SelectOptions extends BaseOptions {
19
+ choices: SelectChoice[];
20
+ }
21
+ export interface CheckboxChoice extends SelectChoice {
22
+ selected?: boolean;
23
+ }
24
+ export interface CheckboxOptions extends BaseOptions {
25
+ choices: CheckboxChoice[];
26
+ min?: number;
27
+ max?: number;
28
+ }
29
+ export interface ConfirmOptions extends BaseOptions {
30
+ initial?: boolean;
31
+ }
package/dist/types.js ADDED
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ /**
3
+ * Type definitions for Mep CLI interactions.
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
package/example.ts ADDED
@@ -0,0 +1,83 @@
1
+ import { MepCLI } from './src'; // Or mepcli if installed via NPM
2
+
3
+ /**
4
+ * Runs a comprehensive set of tests for all MepCLI prompt types.
5
+ */
6
+ async function runAllTests() {
7
+ console.clear();
8
+ console.log("--- MepCLI Comprehensive Test Suite (Neutralized) ---\n");
9
+
10
+ try {
11
+ // --- 1. Text Prompt Test (with Validation) ---
12
+ const projectName = await MepCLI.text({
13
+ message: "Enter the name for your new project:",
14
+ placeholder: "e.g., minimalist-cli-app",
15
+ initial: "MepProject",
16
+ validate: (value) => {
17
+ if (value.length < 3) {
18
+ return "Project name must be at least 3 characters long.";
19
+ }
20
+ if (value.includes('&')) {
21
+ return "Project name cannot contain '&' symbol.";
22
+ }
23
+ return true;
24
+ }
25
+ });
26
+ console.log(`\n✅ Text Result: Project name set to '${projectName}'`);
27
+
28
+ // --- 2. Password Prompt Test ---
29
+ const apiKey = await MepCLI.password({
30
+ message: "Enter the project's external API key:",
31
+ placeholder: "Input will be hidden..."
32
+ });
33
+ // Note: Do not log the actual key in a real app.
34
+ console.log(`\n✅ Password Result: API key entered (length: ${apiKey.length})`);
35
+
36
+
37
+ // --- 3. Select Prompt Test (Single Choice) ---
38
+ const theme = await MepCLI.select({
39
+ message: "Choose your preferred editor color theme:",
40
+ choices: [
41
+ { title: "Dark Mode (Default)", value: "dark" },
42
+ { title: "Light Mode (Classic)", value: "light" },
43
+ { title: "High Contrast (Accessibility)", value: "contrast" },
44
+ { title: "Monokai Pro", value: "monokai" },
45
+ ]
46
+ });
47
+ console.log(`\n✅ Select Result: Chosen theme is: ${theme}`);
48
+
49
+
50
+ // --- 4. Checkbox Prompt Test (Multi-Choice with Min/Max) ---
51
+ const buildTools = await MepCLI.checkbox({
52
+ message: "Select your required bundlers/build tools (Min 1, Max 2):",
53
+ min: 1,
54
+ max: 2,
55
+ choices: [
56
+ { title: "Webpack", value: "webpack" },
57
+ { title: "Vite", value: "vite", selected: true },
58
+ { title: "Rollup", value: "rollup" },
59
+ { title: "esbuild", value: "esbuild" }
60
+ ]
61
+ });
62
+ console.log(`\n✅ Checkbox Result: Selected build tools: [${buildTools.join(', ')}]`);
63
+
64
+
65
+ // --- 5. Confirm Prompt Test ---
66
+ const proceed = await MepCLI.confirm({
67
+ message: "Do you want to continue with the installation setup?",
68
+ initial: true
69
+ });
70
+ console.log(`\n✅ Confirm Result: Setup decision: ${proceed ? 'Proceed' : 'Cancel'}`);
71
+
72
+ console.log("\n--- All MepCLI tests completed successfully! ---");
73
+
74
+ } catch (e) {
75
+ if (e instanceof Error && e.message === 'User force closed') {
76
+ console.log("\nOperation cancelled by user (Ctrl+C).");
77
+ } else {
78
+ console.error("\nAn error occurred during prompt execution:", e);
79
+ }
80
+ }
81
+ }
82
+
83
+ runAllTests();
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "mepcli",
3
+ "version": "0.1.0",
4
+ "description": "Zero-dependency, minimalist interactive CLI prompt for Node.js",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/CodeTease/mep.git"
8
+ },
9
+ "main": "dist/index.js",
10
+ "types": "dist/index.d.ts",
11
+ "files": [
12
+ "dist",
13
+ "README.md",
14
+ "LICENSE",
15
+ "example.ts"
16
+ ],
17
+ "scripts": {
18
+ "build": "tsc",
19
+ "prepublishOnly": "npm run build",
20
+ "test": "ts-node example.ts"
21
+ },
22
+ "keywords": [
23
+ "cli",
24
+ "prompt",
25
+ "inquirer",
26
+ "interactive",
27
+ "zero-dependency",
28
+ "codetease"
29
+ ],
30
+ "author": "CodeTease",
31
+ "license": "MIT",
32
+ "devDependencies": {
33
+ "@types/node": "^20.0.0",
34
+ "ts-node": "^10.9.0",
35
+ "typescript": "^5.0.0"
36
+ }
37
+ }