Projekt

Obecné

Profil

Stáhnout (10.4 KB) Statistiky
| Větev: | Revize:
1 3a515b92 cagy
/*!
2
 * depd
3
 * Copyright(c) 2014-2017 Douglas Christopher Wilson
4
 * MIT Licensed
5
 */
6
7
/**
8
 * Module dependencies.
9
 */
10
11
var callSiteToString = require('./lib/compat').callSiteToString
12
var eventListenerCount = require('./lib/compat').eventListenerCount
13
var relative = require('path').relative
14
15
/**
16
 * Module exports.
17
 */
18
19
module.exports = depd
20
21
/**
22
 * Get the path to base files on.
23
 */
24
25
var basePath = process.cwd()
26
27
/**
28
 * Determine if namespace is contained in the string.
29
 */
30
31
function containsNamespace (str, namespace) {
32
  var vals = str.split(/[ ,]+/)
33
  var ns = String(namespace).toLowerCase()
34
35
  for (var i = 0; i < vals.length; i++) {
36
    var val = vals[i]
37
38
    // namespace contained
39
    if (val && (val === '*' || val.toLowerCase() === ns)) {
40
      return true
41
    }
42
  }
43
44
  return false
45
}
46
47
/**
48
 * Convert a data descriptor to accessor descriptor.
49
 */
50
51
function convertDataDescriptorToAccessor (obj, prop, message) {
52
  var descriptor = Object.getOwnPropertyDescriptor(obj, prop)
53
  var value = descriptor.value
54
55
  descriptor.get = function getter () { return value }
56
57
  if (descriptor.writable) {
58
    descriptor.set = function setter (val) { return (value = val) }
59
  }
60
61
  delete descriptor.value
62
  delete descriptor.writable
63
64
  Object.defineProperty(obj, prop, descriptor)
65
66
  return descriptor
67
}
68
69
/**
70
 * Create arguments string to keep arity.
71
 */
72
73
function createArgumentsString (arity) {
74
  var str = ''
75
76
  for (var i = 0; i < arity; i++) {
77
    str += ', arg' + i
78
  }
79
80
  return str.substr(2)
81
}
82
83
/**
84
 * Create stack string from stack.
85
 */
86
87
function createStackString (stack) {
88
  var str = this.name + ': ' + this.namespace
89
90
  if (this.message) {
91
    str += ' deprecated ' + this.message
92
  }
93
94
  for (var i = 0; i < stack.length; i++) {
95
    str += '\n    at ' + callSiteToString(stack[i])
96
  }
97
98
  return str
99
}
100
101
/**
102
 * Create deprecate for namespace in caller.
103
 */
104
105
function depd (namespace) {
106
  if (!namespace) {
107
    throw new TypeError('argument namespace is required')
108
  }
109
110
  var stack = getStack()
111
  var site = callSiteLocation(stack[1])
112
  var file = site[0]
113
114
  function deprecate (message) {
115
    // call to self as log
116
    log.call(deprecate, message)
117
  }
118
119
  deprecate._file = file
120
  deprecate._ignored = isignored(namespace)
121
  deprecate._namespace = namespace
122
  deprecate._traced = istraced(namespace)
123
  deprecate._warned = Object.create(null)
124
125
  deprecate.function = wrapfunction
126
  deprecate.property = wrapproperty
127
128
  return deprecate
129
}
130
131
/**
132
 * Determine if namespace is ignored.
133
 */
134
135
function isignored (namespace) {
136
  /* istanbul ignore next: tested in a child processs */
137
  if (process.noDeprecation) {
138
    // --no-deprecation support
139
    return true
140
  }
141
142
  var str = process.env.NO_DEPRECATION || ''
143
144
  // namespace ignored
145
  return containsNamespace(str, namespace)
146
}
147
148
/**
149
 * Determine if namespace is traced.
150
 */
151
152
function istraced (namespace) {
153
  /* istanbul ignore next: tested in a child processs */
154
  if (process.traceDeprecation) {
155
    // --trace-deprecation support
156
    return true
157
  }
158
159
  var str = process.env.TRACE_DEPRECATION || ''
160
161
  // namespace traced
162
  return containsNamespace(str, namespace)
163
}
164
165
/**
166
 * Display deprecation message.
167
 */
168
169
function log (message, site) {
170
  var haslisteners = eventListenerCount(process, 'deprecation') !== 0
171
172
  // abort early if no destination
173
  if (!haslisteners && this._ignored) {
174
    return
175
  }
176
177
  var caller
178
  var callFile
179
  var callSite
180
  var depSite
181
  var i = 0
182
  var seen = false
183
  var stack = getStack()
184
  var file = this._file
185
186
  if (site) {
187
    // provided site
188
    depSite = site
189
    callSite = callSiteLocation(stack[1])
190
    callSite.name = depSite.name
191
    file = callSite[0]
192
  } else {
193
    // get call site
194
    i = 2
195
    depSite = callSiteLocation(stack[i])
196
    callSite = depSite
197
  }
198
199
  // get caller of deprecated thing in relation to file
200
  for (; i < stack.length; i++) {
201
    caller = callSiteLocation(stack[i])
202
    callFile = caller[0]
203
204
    if (callFile === file) {
205
      seen = true
206
    } else if (callFile === this._file) {
207
      file = this._file
208
    } else if (seen) {
209
      break
210
    }
211
  }
212
213
  var key = caller
214
    ? depSite.join(':') + '__' + caller.join(':')
215
    : undefined
216
217
  if (key !== undefined && key in this._warned) {
218
    // already warned
219
    return
220
  }
221
222
  this._warned[key] = true
223
224
  // generate automatic message from call site
225
  var msg = message
226
  if (!msg) {
227
    msg = callSite === depSite || !callSite.name
228
      ? defaultMessage(depSite)
229
      : defaultMessage(callSite)
230
  }
231
232
  // emit deprecation if listeners exist
233
  if (haslisteners) {
234
    var err = DeprecationError(this._namespace, msg, stack.slice(i))
235
    process.emit('deprecation', err)
236
    return
237
  }
238
239
  // format and write message
240
  var format = process.stderr.isTTY
241
    ? formatColor
242
    : formatPlain
243
  var output = format.call(this, msg, caller, stack.slice(i))
244
  process.stderr.write(output + '\n', 'utf8')
245
}
246
247
/**
248
 * Get call site location as array.
249
 */
250
251
function callSiteLocation (callSite) {
252
  var file = callSite.getFileName() || '<anonymous>'
253
  var line = callSite.getLineNumber()
254
  var colm = callSite.getColumnNumber()
255
256
  if (callSite.isEval()) {
257
    file = callSite.getEvalOrigin() + ', ' + file
258
  }
259
260
  var site = [file, line, colm]
261
262
  site.callSite = callSite
263
  site.name = callSite.getFunctionName()
264
265
  return site
266
}
267
268
/**
269
 * Generate a default message from the site.
270
 */
271
272
function defaultMessage (site) {
273
  var callSite = site.callSite
274
  var funcName = site.name
275
276
  // make useful anonymous name
277
  if (!funcName) {
278
    funcName = '<anonymous@' + formatLocation(site) + '>'
279
  }
280
281
  var context = callSite.getThis()
282
  var typeName = context && callSite.getTypeName()
283
284
  // ignore useless type name
285
  if (typeName === 'Object') {
286
    typeName = undefined
287
  }
288
289
  // make useful type name
290
  if (typeName === 'Function') {
291
    typeName = context.name || typeName
292
  }
293
294
  return typeName && callSite.getMethodName()
295
    ? typeName + '.' + funcName
296
    : funcName
297
}
298
299
/**
300
 * Format deprecation message without color.
301
 */
302
303
function formatPlain (msg, caller, stack) {
304
  var timestamp = new Date().toUTCString()
305
306
  var formatted = timestamp +
307
    ' ' + this._namespace +
308
    ' deprecated ' + msg
309
310
  // add stack trace
311
  if (this._traced) {
312
    for (var i = 0; i < stack.length; i++) {
313
      formatted += '\n    at ' + callSiteToString(stack[i])
314
    }
315
316
    return formatted
317
  }
318
319
  if (caller) {
320
    formatted += ' at ' + formatLocation(caller)
321
  }
322
323
  return formatted
324
}
325
326
/**
327
 * Format deprecation message with color.
328
 */
329
330
function formatColor (msg, caller, stack) {
331
  var formatted = '\x1b[36;1m' + this._namespace + '\x1b[22;39m' + // bold cyan
332
    ' \x1b[33;1mdeprecated\x1b[22;39m' + // bold yellow
333
    ' \x1b[0m' + msg + '\x1b[39m' // reset
334
335
  // add stack trace
336
  if (this._traced) {
337
    for (var i = 0; i < stack.length; i++) {
338
      formatted += '\n    \x1b[36mat ' + callSiteToString(stack[i]) + '\x1b[39m' // cyan
339
    }
340
341
    return formatted
342
  }
343
344
  if (caller) {
345
    formatted += ' \x1b[36m' + formatLocation(caller) + '\x1b[39m' // cyan
346
  }
347
348
  return formatted
349
}
350
351
/**
352
 * Format call site location.
353
 */
354
355
function formatLocation (callSite) {
356
  return relative(basePath, callSite[0]) +
357
    ':' + callSite[1] +
358
    ':' + callSite[2]
359
}
360
361
/**
362
 * Get the stack as array of call sites.
363
 */
364
365
function getStack () {
366
  var limit = Error.stackTraceLimit
367
  var obj = {}
368
  var prep = Error.prepareStackTrace
369
370
  Error.prepareStackTrace = prepareObjectStackTrace
371
  Error.stackTraceLimit = Math.max(10, limit)
372
373
  // capture the stack
374
  Error.captureStackTrace(obj)
375
376
  // slice this function off the top
377
  var stack = obj.stack.slice(1)
378
379
  Error.prepareStackTrace = prep
380
  Error.stackTraceLimit = limit
381
382
  return stack
383
}
384
385
/**
386
 * Capture call site stack from v8.
387
 */
388
389
function prepareObjectStackTrace (obj, stack) {
390
  return stack
391
}
392
393
/**
394
 * Return a wrapped function in a deprecation message.
395
 */
396
397
function wrapfunction (fn, message) {
398
  if (typeof fn !== 'function') {
399
    throw new TypeError('argument fn must be a function')
400
  }
401
402
  var args = createArgumentsString(fn.length)
403
  var deprecate = this // eslint-disable-line no-unused-vars
404
  var stack = getStack()
405
  var site = callSiteLocation(stack[1])
406
407
  site.name = fn.name
408
409
   // eslint-disable-next-line no-eval
410
  var deprecatedfn = eval('(function (' + args + ') {\n' +
411
    '"use strict"\n' +
412
    'log.call(deprecate, message, site)\n' +
413
    'return fn.apply(this, arguments)\n' +
414
    '})')
415
416
  return deprecatedfn
417
}
418
419
/**
420
 * Wrap property in a deprecation message.
421
 */
422
423
function wrapproperty (obj, prop, message) {
424
  if (!obj || (typeof obj !== 'object' && typeof obj !== 'function')) {
425
    throw new TypeError('argument obj must be object')
426
  }
427
428
  var descriptor = Object.getOwnPropertyDescriptor(obj, prop)
429
430
  if (!descriptor) {
431
    throw new TypeError('must call property on owner object')
432
  }
433
434
  if (!descriptor.configurable) {
435
    throw new TypeError('property must be configurable')
436
  }
437
438
  var deprecate = this
439
  var stack = getStack()
440
  var site = callSiteLocation(stack[1])
441
442
  // set site name
443
  site.name = prop
444
445
  // convert data descriptor
446
  if ('value' in descriptor) {
447
    descriptor = convertDataDescriptorToAccessor(obj, prop, message)
448
  }
449
450
  var get = descriptor.get
451
  var set = descriptor.set
452
453
  // wrap getter
454
  if (typeof get === 'function') {
455
    descriptor.get = function getter () {
456
      log.call(deprecate, message, site)
457
      return get.apply(this, arguments)
458
    }
459
  }
460
461
  // wrap setter
462
  if (typeof set === 'function') {
463
    descriptor.set = function setter () {
464
      log.call(deprecate, message, site)
465
      return set.apply(this, arguments)
466
    }
467
  }
468
469
  Object.defineProperty(obj, prop, descriptor)
470
}
471
472
/**
473
 * Create DeprecationError for deprecation
474
 */
475
476
function DeprecationError (namespace, message, stack) {
477
  var error = new Error()
478
  var stackString
479
480
  Object.defineProperty(error, 'constructor', {
481
    value: DeprecationError
482
  })
483
484
  Object.defineProperty(error, 'message', {
485
    configurable: true,
486
    enumerable: false,
487
    value: message,
488
    writable: true
489
  })
490
491
  Object.defineProperty(error, 'name', {
492
    enumerable: false,
493
    configurable: true,
494
    value: 'DeprecationError',
495
    writable: true
496
  })
497
498
  Object.defineProperty(error, 'namespace', {
499
    configurable: true,
500
    enumerable: false,
501
    value: namespace,
502
    writable: true
503
  })
504
505
  Object.defineProperty(error, 'stack', {
506
    configurable: true,
507
    enumerable: false,
508
    get: function () {
509
      if (stackString !== undefined) {
510
        return stackString
511
      }
512
513
      // prepare stack trace
514
      return (stackString = createStackString.call(this, stack))
515
    },
516
    set: function setter (val) {
517
      stackString = val
518
    }
519
  })
520
521
  return error
522
}