twitst4tz

twitter statistics web application
Log | Files | Refs | README | LICENSE

index.js (10669B)


      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 }