summaryrefslogtreecommitdiff
path: root/deps/npm/lib/utils/output.js
blob: b705153ad5a9f07e2c6c22a21ac4977a9fe66e26 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157

// centralized stdout writer.

exports.doColor = doColor
exports.write = write

var npm = require("../npm.js")
  , tty = require("tty")
  , streams = {}
  , ttys = {}
  , net = require("net")
  , util = require("util")
  , deadStreams = {}

function doColor (stream) {
  var conf = npm.config.get("color")
  return (!conf) ? false
       : (conf === "always") ? true
       : isatty(stream)
}
function isatty (stream) {
  // console.error("isatty?", stream)
  if (!tty.isatty) return true
  if (!stream) return false
  if (stream.isTTY) return true
  if (stream && (typeof stream.fd === "number")) {
    stream.isTTY = tty.isatty(stream.fd)
  }
  return stream.isTTY
}

function write (args, stream, lf, cb) {
  // console.error("write", [args, stream, lf, cb])
  if (typeof cb !== "function" && typeof lf === "function") {
    cb = lf
    lf = null
  }
  if (typeof cb !== "function" && typeof stream === "function") {
    cb = stream
    stream = npm.config.get("outfd")
  }

  stream = getStream(stream)
  // console.error("gotStream", stream)
  if (lf == null) lf = isatty(stream)
  if (!stream) return cb && cb(), false
  if (!Array.isArray(args)) args = [args]

  // console.error("write", args)

  var msg = ""
    , colored = doColor(stream)
  msg = args.map(function (arg) {
    if (typeof arg !== "string") {
      return util.inspect(arg, false, 5, colored) + "\n"
    }
    if (!colored) arg = arg.replace(/\033\[[0-9;]*m/g, '')
    if (!npm.config.get("unicode")) {
      arg = arg.replace(/└/g, "`")
               .replace(/─/g, "-")
               .replace(/│/g, "|")
               .replace(/├/g, "+")
               .replace(/┬/g, "-")
    }
    return arg
  }).join(" ")

  // listen to the "output" event to cancel/modify/redirect
  npm.output = {stream:stream, message:msg}
  npm.emit("output", npm.output)
  if (!npm.output) return cb && cb(), false // cancelled
  stream = npm.output.stream
  msg = npm.output.message

  // EPIPE errors just mean that the stream is not listening
  // any more.  Mark the stream as dead, and return.
  if (deadStreams[stream.fd]) {
    return cb && cb(), false
  }
  if (!deadStreams.hasOwnProperty(stream.fd)) {
    deadStreams[stream.fd] = false
    stream.on("error", function (er) {
      if (er.code === "EPIPE") {
        deadStreams[stream.fd] = true
        return cb && cb()
      }
      if (stream.listeners("error").length === 1) {
        throw er
      }
    })
  }

  // use the \r\n in case we're in raw mode.
  msg = msg.split(/\r?\n/).concat("").join(lf ? "\r\n" : "\n")
  // output to stderr should be synchronous
  if (stream === process.stderr || stream.fd === 2) {
    process.stderr.write(msg)
    if (cb) cb()
    return true
  }
  // console.error("writing ", msg)
  var flushed = stream.write(msg)
  if (flushed && cb) {
    process.nextTick(cb)
  } else if (cb) {
    stream.once("drain", cb)
  }
  return flushed
}

var hadError = false
function getStream (fd) {
  if (hadError) return

  var stream
  if (!fd && fd !== 0) return
  if (typeof fd === "string") fd = +fd

  // console.error("getStream", fd, hadError)

  if (fd && typeof fd === "object") {
    stream = fd
    fd = fd.fd
  } else if (streams[fd]) {
    stream = streams[fd]
  } else {
    switch (fd) {
      case 1:
        stream = process.stdout
        stream.fd = fd
        stream.writable = true
        break
      case 2:
        stream = process.stderr
        stream.fd = fd
        stream.writable = true
        break
      default:
        try {
          stream = new net.Stream(fd)
          if (!stream || !stream.writable) {
            throw new Error("Stream not writable")
          }
        } catch (ex) {
          // if this fails, then regular logging is most likely broken.
          var er = new Error("cannot output to fd "+fd + ": "+
                             (ex.stack || ex.message).substr(7) + "\n")
          console.error(er.stack)
          hadError = true
          process.exit(1)
        }
    }
  }

  if (!stream || !stream.writable) return
  return streams[fd] = stream
}