next-anteater 0.2.1 → 0.2.3
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/lib/setup.mjs +2 -1
- package/lib/ui.mjs +71 -14
- package/package.json +1 -1
package/lib/setup.mjs
CHANGED
|
@@ -9,7 +9,7 @@ import { execSync } from "node:child_process";
|
|
|
9
9
|
import {
|
|
10
10
|
bold, dim, green, red, yellow, cyan,
|
|
11
11
|
ok, fail, warn, info, heading, blank,
|
|
12
|
-
ask, confirm, select, spinner,
|
|
12
|
+
ask, confirm, select, spinner, closeRL,
|
|
13
13
|
} from "./ui.mjs";
|
|
14
14
|
import { detectProject } from "./detect.mjs";
|
|
15
15
|
import { scaffoldFiles } from "./scaffold.mjs";
|
|
@@ -314,6 +314,7 @@ export async function main() {
|
|
|
314
314
|
else warn("Test dispatch failed \u2014 check GitHub Actions");
|
|
315
315
|
|
|
316
316
|
// ─── Done! ──────────────────────────────────────────────────
|
|
317
|
+
closeRL();
|
|
317
318
|
blank();
|
|
318
319
|
console.log(` ${bold(green("\u{1F41C} Anteater is ready."))}`);
|
|
319
320
|
blank();
|
package/lib/ui.mjs
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Terminal UI helpers — zero dependencies.
|
|
3
|
+
*
|
|
4
|
+
* Handles both interactive (TTY) and piped (file/pipe) input.
|
|
5
|
+
* When stdin is piped, pre-reads all lines and serves them sequentially.
|
|
3
6
|
*/
|
|
4
7
|
import * as readline from "node:readline";
|
|
5
8
|
|
|
@@ -19,38 +22,92 @@ export const info = (msg) => console.log(` ${dim(msg)}`);
|
|
|
19
22
|
export const heading = (msg) => console.log(`\n ${bold(msg)}\n ${"─".repeat(msg.length)}`);
|
|
20
23
|
export const blank = () => console.log();
|
|
21
24
|
|
|
25
|
+
// ─── Piped input support ────────────────────────────────────────
|
|
26
|
+
// When stdin is piped (not a TTY), pre-read all lines into a queue.
|
|
27
|
+
// This avoids readline issues where EOF closes the interface mid-setup.
|
|
28
|
+
|
|
29
|
+
let _pipedLines = null;
|
|
30
|
+
let _pipedReady = null;
|
|
31
|
+
|
|
32
|
+
if (!process.stdin.isTTY) {
|
|
33
|
+
_pipedLines = [];
|
|
34
|
+
_pipedReady = new Promise((resolve) => {
|
|
35
|
+
const rl = readline.createInterface({ input: process.stdin });
|
|
36
|
+
rl.on("line", (line) => _pipedLines.push(line));
|
|
37
|
+
rl.on("close", resolve);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let _pipedIdx = 0;
|
|
42
|
+
|
|
43
|
+
async function nextPipedLine() {
|
|
44
|
+
await _pipedReady;
|
|
45
|
+
if (_pipedIdx < _pipedLines.length) {
|
|
46
|
+
return _pipedLines[_pipedIdx++];
|
|
47
|
+
}
|
|
48
|
+
return "";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ─── Interactive readline (TTY only) ────────────────────────────
|
|
52
|
+
|
|
53
|
+
let _rl = null;
|
|
54
|
+
|
|
55
|
+
function getRL() {
|
|
56
|
+
if (!_rl) {
|
|
57
|
+
_rl = readline.createInterface({
|
|
58
|
+
input: process.stdin,
|
|
59
|
+
output: process.stdout,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return _rl;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function closeRL() {
|
|
66
|
+
if (_rl) {
|
|
67
|
+
_rl.close();
|
|
68
|
+
_rl = null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
22
72
|
/**
|
|
23
73
|
* Prompt the user for text input.
|
|
24
74
|
*/
|
|
25
75
|
export function ask(question, { mask = false } = {}) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
76
|
+
// Piped mode — read next line from pre-buffered input
|
|
77
|
+
if (_pipedLines) {
|
|
78
|
+
const prompt = ` ${cyan("?")} ${question} `;
|
|
79
|
+
process.stdout.write(prompt);
|
|
80
|
+
return nextPipedLine().then((line) => {
|
|
81
|
+
process.stdout.write("\n");
|
|
82
|
+
return line.trim();
|
|
30
83
|
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Interactive mode — use readline
|
|
87
|
+
return new Promise((resolve) => {
|
|
88
|
+
const rl = getRL();
|
|
31
89
|
|
|
32
|
-
// If masking, mute output and write dots
|
|
33
90
|
if (mask) {
|
|
34
91
|
const origWrite = process.stdout.write.bind(process.stdout);
|
|
92
|
+
const restore = () => { process.stdout.write = origWrite; };
|
|
93
|
+
|
|
35
94
|
process.stdout.write = (chunk, encoding, cb) => {
|
|
36
|
-
// Let the question prompt through, mask everything after
|
|
37
95
|
if (typeof chunk === "string" && chunk.includes(question)) {
|
|
38
96
|
return origWrite(chunk, encoding, cb);
|
|
39
97
|
}
|
|
40
|
-
// Replace characters with bullets
|
|
41
98
|
const masked = typeof chunk === "string" ? chunk.replace(/[^\r\n]/g, "•") : chunk;
|
|
42
99
|
return origWrite(masked, encoding, cb);
|
|
43
100
|
};
|
|
44
101
|
|
|
45
|
-
rl.
|
|
46
|
-
|
|
102
|
+
rl.question(` ${cyan("?")} ${question} `, (answer) => {
|
|
103
|
+
restore();
|
|
104
|
+
resolve(answer.trim());
|
|
105
|
+
});
|
|
106
|
+
} else {
|
|
107
|
+
rl.question(` ${cyan("?")} ${question} `, (answer) => {
|
|
108
|
+
resolve(answer.trim());
|
|
47
109
|
});
|
|
48
110
|
}
|
|
49
|
-
|
|
50
|
-
rl.question(` ${cyan("?")} ${question} `, (answer) => {
|
|
51
|
-
rl.close();
|
|
52
|
-
resolve(answer.trim());
|
|
53
|
-
});
|
|
54
111
|
});
|
|
55
112
|
}
|
|
56
113
|
|