read.js
  1  
  2  module.exports = read
  3  
  4  var readline = require('readline')
  5  var Mute = require('mute-stream')
  6  
  7  function 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  }