• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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