Projekt

Obecné

Profil

Stáhnout (10.4 KB) Statistiky
| Větev: | Revize:
1
/*!
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
}
(4-4/5)