/* Copyright (c) 2014-2015 Richard Rodger, MIT License */
/* jshint node:true, asi:true, eqnull:true *//* Copyright (c) 2014-2015 Richard Rodger, MIT License */
/* jshint node:true, asi:true, eqnull:true */Create JavaScript Error objects with code strings, context details, and templated messages.
"use strict";var util = require('util')var _ = require('lodash')module.exports = eraroParameters:
Returns: Function
The created function has parameters:
and returns an Error object (to be thrown or used in a callback, as needed). The returned Error object has the following additional properties:
function eraro( options ) {
options = options || {}
var msgprefix = false === options.prefix ? '' :
(_.isString(options.prefix) ? options.prefix : _.isString(options.package) ? options.package+': ' : '')
var packaje = options.package || 'unknown'
var callmodule = options.module || module
var msgmap = options.msgmap || {}
var inspect = null == options.inspect ? true : !!options.inspect
var markers = [module.filename]
var filename = callmodule.filename
if( filename ) markers.push(filename);
var errormaker = function( ex, code, msg, details ) {
var internalex = false
if( util.isError(ex) ) {
if( ex.eraro && !options.override ) return ex;
}
else {
internalex = true
ex = null
code = arguments[0]
msg = arguments[1]
details = arguments[2]
}
code = _.isString(code) ? code :
(ex ?
ex.code ? ex.code :
ex.message ? ex.message :
'unknown' : 'unknown')
details = _.isObject(details) ? details :
(_.isObject(msg) && !_.isString(msg) ? msg : {})
msg = _.isString(msg) ? msg : null
msg = buildmessage(options,msg,msgmap,msgprefix,inspect,code,details,ex)
var err = new Error(msg)
if( ex ) {
details.orig$ = null == details.orig$ ? ex : details.orig$
details.message$ = null == details.message$ ? ex.message : details.message$drag along properties from original exception
for( var p in ex ) {
err[p] = ex[p]
}
}
err.eraro = true
err.orig = ex // orig
err.code = code
err[packaje] = true
err.package = packaje
err.msg = msg
err.details = details
err.stack = ex ? ex.stack : err.stack
err.callpoint = callpoint( err , markers )
return err;
}
errormaker.callpoint = callpoint
return errormaker;
}Parameters:
Returns: String; stack trace line, with indent removed
function callpoint( error, markers ) {
markers = _.isArray(markers) ? markers : []
var stack = error ? error.stack : null
var out = ''
if( stack ) {
var lines = stack.split('\n')
var done = false
var i
line_loop:
for( i = 1; i < lines.length; i++ ) {
var line = lines[i]
var found = false
for( var j = 0; j < markers.length; j++ ) {
if( _.isString( markers[j] ) ) {
found = ( -1 != line.indexOf( markers[j] ) )
if( found ) break;
}
}
if( !found ) break line_loop;
}
out = _.isString(lines[i]) ? lines[i].substring(4) : out
}
return out
}Uses the underscore template function with default settings. The original message (msg) has priority over messages from the msgmap. If no message can be found, the code is used as a message. If an insert property is not defined, it is replaced with [name?] in the message. As a convenience, util and _ are made available in the templates.
Parameters:
Returns: String; human readable error message
function buildmessage(options,msg,msgmap,msgprefix,inspect,code,details,ex) {
var message = msgprefix + (_.isString(msg) ? msg :
_.isString(msgmap[code]) ? msgmap[code] :
ex ? originalmsg(options.override,ex) : code )These are the inserts.
var valmap = _.extend({},details,{code:code})Workaround to prevent underscore blowing up if properties are not found. Reserved words and undefined need to be suffixed with $ in the template interpolates.
var valstrmap = {util:util,_:_}
_.each(valmap,function(val,key){
/* jshint evil:true */
try { eval('var '+key+';') } catch(e) { key = key+'$' }
if( {'undefined':1,'NaN':1}[key] ) { key = key+'$' }
valstrmap[key] = inspect && !_.isString(val) ? util.inspect(val) : val
})
var done = false
while( !done ) {
try {
var tm = _.template( message )
message = tm(valstrmap)
done = true
}
catch(e) {
if(e instanceof ReferenceError) {
var m = /ReferenceError:\s+(.*?)\s+/.exec(e.toString())
if( m && m[1] ) {
valstrmap[m[1]]="["+m[1]+"?]"
}
else done = true
}Some other error - give up and just dump the properties at the end of the template.
else {
done = true
message = message+' VALUES:'+util.inspect(valmap,{depth:2})+
' TEMPLATE ERROR: '+e
}
}
}
return message
}
function originalmsg( override, ex ) {
if( !ex ) return;
if( override && ex.eraro && ex.orig ) return ex.orig.message;
return ex.message;
}