96 lines
2.4 KiB
JavaScript
96 lines
2.4 KiB
JavaScript
/**
|
|
* Line-oriented stdin for Bare/Pear (no node:readline) and Node fallback.
|
|
* Pear-terminal uses bare-readline + bare-stdio; we mirror that after Node readline.
|
|
*/
|
|
|
|
/**
|
|
* @param {import('stream').Readable} stdin
|
|
* @param {import('stream').Writable} stdout
|
|
* @returns {(prompt: string) => Promise<string>}
|
|
*/
|
|
export async function createBareReadlineQuestion(stdin, stdout) {
|
|
const mod = await import('bare-readline')
|
|
const createInterface = mod.createInterface ?? mod.default?.createInterface
|
|
if (typeof createInterface !== 'function') {
|
|
throw new Error('bare-readline: createInterface missing')
|
|
}
|
|
const rl = createInterface({
|
|
input: stdin,
|
|
output: stdout,
|
|
prompt: ''
|
|
})
|
|
return (prompt) =>
|
|
new Promise((resolve) => {
|
|
rl.setPrompt(prompt)
|
|
rl.prompt()
|
|
rl.once('line', (line) => {
|
|
resolve(line)
|
|
})
|
|
})
|
|
}
|
|
|
|
/**
|
|
* @param {import('stream').Readable} stdin
|
|
* @param {import('stream').Writable} stdout
|
|
* @returns {(prompt: string) => Promise<string | null>}
|
|
*/
|
|
export function createStreamLineReader(stdin, stdout) {
|
|
let buf = ''
|
|
/** @type {string[]} */
|
|
const queue = []
|
|
/** @type {((line: string | null) => void)[]} */
|
|
const waiters = []
|
|
|
|
function deliver(line) {
|
|
if (waiters.length) waiters.shift()(line)
|
|
else queue.push(line)
|
|
}
|
|
|
|
function onData(chunk) {
|
|
const s =
|
|
typeof chunk === 'string' ? chunk : Buffer.from(chunk).toString('utf8')
|
|
buf += s
|
|
let i
|
|
while ((i = buf.indexOf('\n')) !== -1) {
|
|
const raw = buf.slice(0, i)
|
|
buf = buf.slice(i + 1)
|
|
deliver(raw.replace(/\r$/, ''))
|
|
}
|
|
}
|
|
|
|
function flushRemainder() {
|
|
if (buf.length) {
|
|
deliver(buf.replace(/\r$/, ''))
|
|
buf = ''
|
|
}
|
|
}
|
|
|
|
function onEnd() {
|
|
flushRemainder()
|
|
while (waiters.length) waiters.shift()(null)
|
|
}
|
|
|
|
if (typeof stdin.setEncoding === 'function') stdin.setEncoding('utf8')
|
|
stdin.on('data', onData)
|
|
stdin.on('end', onEnd)
|
|
stdin.on('error', onEnd)
|
|
if (typeof stdin.resume === 'function') stdin.resume()
|
|
|
|
return function readLine(prompt) {
|
|
if (typeof stdout.write === 'function') stdout.write(prompt)
|
|
if (queue.length) return Promise.resolve(queue.shift() ?? null)
|
|
return new Promise((resolve) => waiters.push(resolve))
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @returns {boolean}
|
|
*/
|
|
export function looksLikeInteractiveStdin(stdin) {
|
|
return Boolean(
|
|
stdin &&
|
|
typeof stdin.on === 'function' &&
|
|
typeof stdin.resume === 'function'
|
|
)
|
|
}
|