1 2module.exports = read 3 4var readline = require('readline') 5var Mute = require('mute-stream') 6 7function read (opts, cb) { 8 if (opts.num) { 9 throw new Error('read() no longer accepts a char number limit') 10 } 11 12 if (typeof opts.default !== 'undefined' && 13 typeof opts.default !== 'string' && 14 typeof opts.default !== 'number') { 15 throw new Error('default value must be string or number') 16 } 17 18 var input = opts.input || process.stdin 19 var output = opts.output || process.stdout 20 var prompt = (opts.prompt || '').trim() + ' ' 21 var silent = opts.silent 22 var editDef = false 23 var timeout = opts.timeout 24 25 var def = opts.default || '' 26 if (def) { 27 if (silent) { 28 prompt += '(<default hidden>) ' 29 } else if (opts.edit) { 30 editDef = true 31 } else { 32 prompt += '(' + def + ') ' 33 } 34 } 35 var terminal = !!(opts.terminal || output.isTTY) 36 37 var m = new Mute({ replace: opts.replace, prompt: prompt }) 38 m.pipe(output, {end: false}) 39 output = m 40 var rlOpts = { input: input, output: output, terminal: terminal } 41 42 if (process.version.match(/^v0\.6/)) { 43 var rl = readline.createInterface(rlOpts.input, rlOpts.output) 44 } else { 45 var rl = readline.createInterface(rlOpts) 46 } 47 48 49 output.unmute() 50 rl.setPrompt(prompt) 51 rl.prompt() 52 if (silent) { 53 output.mute() 54 } else if (editDef) { 55 rl.line = def 56 rl.cursor = def.length 57 rl._refreshLine() 58 } 59 60 var called = false 61 rl.on('line', onLine) 62 rl.on('error', onError) 63 64 rl.on('SIGINT', function () { 65 rl.close() 66 onError(new Error('canceled')) 67 }) 68 69 var timer 70 if (timeout) { 71 timer = setTimeout(function () { 72 onError(new Error('timed out')) 73 }, timeout) 74 } 75 76 function done () { 77 called = true 78 rl.close() 79 80 if (process.version.match(/^v0\.6/)) { 81 rl.input.removeAllListeners('data') 82 rl.input.removeAllListeners('keypress') 83 rl.input.pause() 84 } 85 86 clearTimeout(timer) 87 output.mute() 88 output.end() 89 } 90 91 function onError (er) { 92 if (called) return 93 done() 94 return cb(er) 95 } 96 97 function onLine (line) { 98 if (called) return 99 if (silent && terminal) { 100 output.unmute() 101 output.write('\r\n') 102 } 103 done() 104 // truncate the \n at the end. 105 line = line.replace(/\r?\n$/, '') 106 var isDefault = !!(editDef && line === def) 107 if (def && !line) { 108 isDefault = true 109 line = def 110 } 111 cb(null, line, isDefault) 112 } 113} 114