Comparing version 0.1.3 to 0.1.4
201
eraro.js
/* Copyright (c) 2014 Richard Rodger, MIT License */ | ||
"use strict"; | ||
/* jshint node:true, asi:true, eqnull:true */ | ||
var util = require('util') | ||
// Create JavaScript Error objects with code strings, context details, and templated messages. | ||
"use strict"; | ||
// Remove unwanted lines containing markers | ||
function cleanstack( error, markers ) { | ||
var stack = error ? error.stack : null | ||
var out = '' | ||
if( stack ) { | ||
var lines = stack.split('\n') | ||
var done = false | ||
// #### System modules | ||
var util = require('util') | ||
line_loop: | ||
for( var i = 1; i < lines.length; i++ ) { | ||
var line = lines[i] | ||
var found = false | ||
for( var j = 0; j < markers.length; j++ ) { | ||
found = ( -1 != line.indexOf( markers[j] ) ) | ||
if( found ) break; | ||
} | ||
if( !found ) break line_loop;; | ||
} | ||
// #### External modules | ||
var _ = require('underscore') | ||
out = ([lines[0]].concat(lines.slice(i))).join('\n') | ||
} | ||
return out | ||
} | ||
// #### Exports | ||
module.exports = eraro | ||
// make an error function | ||
module.exports = function( options ) { | ||
// #### Create an _eraro_ function | ||
// Parameters: | ||
// | ||
// * _options_ : (optional) Object; properties: | ||
// * _package_ : (optional) String; package name to mark Error objects | ||
// * _module_ : (optional) Object; _module_ object to use as starting point for _require_ calls | ||
// * _msgmap_ : (optional) Object; map codes to message templates | ||
// | ||
// Returns: Function | ||
// | ||
// The created function has parameters: | ||
// | ||
// * _exception_ : (optional) Error; the original exception to be wrapped | ||
// * _code_ : (optional) String; code value | ||
// * _message_ : (optional) String; error message, will be processed as a template | ||
// * _details_ : (optional) Object; contextual details of the error, used to insert details into message | ||
// | ||
// and returns an Error object (to be thrown or used in a callback, as needed). | ||
// The returned Error object has the following additional properties: | ||
// | ||
// * _code_: String; the code string | ||
// * _package_: String; the package name | ||
// * _**package-name**_: Boolean (true); a convenience marker for the package | ||
// * _msg_: String; the generated message, may differ from original exception message (if any) | ||
// * _details_: Object; contextual details of error | ||
// * _callpoint_: String; first line of stacktrace that is external to eraro and calling module | ||
function eraro( options ) { | ||
options = options || {} | ||
var msgprefix = options.package ? options.package+': ' : '' | ||
var packaje = options.package || 'unknown' | ||
var markers = options.markers || [] | ||
var msgprefix = options.package ? options.package+': ' : '' | ||
var packaje = options.package || 'unknown' | ||
var callmodule = options.module || module | ||
var msgmap = options.msgmap || {} | ||
var filename = module.filename | ||
if( filename ) markers.push(filename) | ||
var markers = [module.filename] | ||
var parentfilename = module.parent ? module.parent.filename : null | ||
if( parentfilename ) markers.push(parentfilename) | ||
var filename = callmodule.filename | ||
if( filename ) markers.push(filename); | ||
@@ -65,11 +77,6 @@ | ||
code = 'string' === typeof(code) ? code : 'unknown' | ||
code = _.isString(code) ? code : 'unknown' | ||
details = _.isObject(details) ? details : (_.isObject(msg) && !_.isString(msg) ? msg : {}) | ||
msg = buildmessage(msg,msgmap,msgprefix,code,details) | ||
details = | ||
('object' === typeof(details)) ? | ||
details : | ||
('object' === typeof(msg) && 'string' !== typeof(msg) ? msg : {}); | ||
msg = msgprefix + ('string' === typeof(msg) ? msg : code) | ||
if( !ex ) { | ||
@@ -79,16 +86,114 @@ ex = new Error(msg) | ||
ex[packaje] = true | ||
ex.package = packaje | ||
ex.code = code | ||
ex[packaje] = true | ||
ex.package = packaje | ||
ex.msg = msg | ||
ex.details = details | ||
ex.callpoint = callpoint( ex, markers ) | ||
ex.code = code | ||
ex.details = details || {} | ||
return ex; | ||
} | ||
} | ||
// clean the stack | ||
if( internalex ) { | ||
ex.stack = cleanstack( ex, markers ) | ||
// #### Find the first external stack trace line. | ||
// Parameters: | ||
// | ||
// * _error_ : (optional) Error; provides the stack | ||
// * _markers_ : (optional) Array[String]; ignore lines containing these strings | ||
// | ||
// 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; | ||
} | ||
return ex; | ||
out = lines[i].substring(4) | ||
} | ||
return out | ||
} | ||
// #### Build the message string from a template by inserting details | ||
// 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: | ||
// | ||
// * _msg_ : (required) String; message template | ||
// * _msgmap_ : (required) Object; map codes to message templates | ||
// * _msgprefix_: (required) String; prefix for all messages, useful as indentification of error origin | ||
// * _code_: (required) String; error code | ||
// * _details_: (required) Object; error details providing context | ||
// | ||
// Returns: String; human readable error message | ||
function buildmessage(msg,msgmap,msgprefix,code,details) { | ||
var message = msgprefix + (_.isString(msg) ? msg : _.isString(msgmap[code]) ? msgmap[code] : 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] = val | ||
}) | ||
var done = false | ||
while( !done ) { | ||
try { | ||
message = _.template( message, 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 | ||
} | ||
{ | ||
"name": "eraro", | ||
"version": "0.1.3", | ||
"description": "Create JavaScript Error objects with code strings, context details, and uncluttered stacktraces", | ||
"version": "0.1.4", | ||
"description": "Create JavaScript Error objects with code strings, context details, and templated messages.", | ||
"main": "eraro.js", | ||
"scripts": { | ||
"test": "node test/eraro.test.js" | ||
"test": "node test/eraro.test.js", | ||
"jshint": "./node_modules/.bin/jshint eraro.js", | ||
"docco": "./node_modules/.bin/docco eraro.js -o doc", | ||
"gh-pages-doc": "cp -r doc/* ../gh-pages/eraro/doc" | ||
}, | ||
@@ -22,3 +25,16 @@ "repository": { | ||
}, | ||
"homepage": "https://github.com/rjrodger/eraro" | ||
"homepage": "https://github.com/rjrodger/eraro", | ||
"dependencies": { | ||
"underscore": "~1.6.0" | ||
}, | ||
"devDependencies": { | ||
"mocha": "~1.18.2", | ||
"docco": "~0.6.3", | ||
"jshint": "~2.5.0" | ||
}, | ||
"files": [ | ||
"README.md", | ||
"LICENSE.txt", | ||
"eraro.js" | ||
] | ||
} |
143
README.md
eraro | ||
===== | ||
### Create JavaScript Error objects with code strings, context details, and uncluttered stacktraces | ||
#### Create JavaScript Error objects with code strings, context details, and templated messages. | ||
For use in library modules to generate contextual errors. Your library | ||
module can return an error code for programmatic inspection by calling | ||
code, and error details as a context object for custom messages and | ||
fault inspection. | ||
There is [annotated source code](http://rjrodger.github.io/eraro/doc/eraro.html) for this module. | ||
Stack trace lines referring to _eraro_ itself, and your library, are | ||
removed. This means that the first line of the stack trace refers to | ||
the position in user code where your library was called. | ||
For use in library modules to generate contextual errors with useful | ||
meta data. Your library module can throw or pass (to a callback) an | ||
_Error_ object that has additional properties, such as a _code_, that | ||
can be used for programmatic inspection by client code that uses your | ||
library. | ||
# Quick example | ||
```JavaScript | ||
var error = require('eraro')() | ||
var error = require('eraro')({package:'mylib'}) | ||
// throw an Error object that has a code | ||
throw error('code_string') | ||
@@ -29,23 +30,125 @@ | ||
throw error('code_string', {foo:1, bar:2}) | ||
// extend an existing Error object | ||
var ex = new Error('Another message.') | ||
throw error(ex,'code_string',{zed:3}) | ||
``` | ||
In all these cases, the Error object will have a _code_ property with value _"code_string"_. | ||
// extend and existing Error object | ||
var ex = new Error('Another message.') | ||
throw error(ex,'code_string',{details:'data'}) | ||
# Usage | ||
Use this module when you are writing a library that will be used by | ||
application code. It allows your library to generate informative error messages. | ||
The module itself is a generator function (taking options) that | ||
returns the error-creating function that you will actually use. Thus | ||
the most common way to use _eraro_ is to require and call immediately: | ||
```JavaScript | ||
var error = require('eraro')({package:'mylib'}) | ||
``` | ||
The Error object has the following additional properties: | ||
The _error_ function can then be used in your library code. The | ||
_error_ function generates _Error_ objects, which can be thrown or used in callbacks: | ||
* _code_: code string | ||
* _details_: context details object | ||
```JavaScript | ||
throw error('code1') | ||
function doStuff( input, callback ) { | ||
if( bad( input ) ) return callback( error('code2') ); | ||
} | ||
``` | ||
The _package_ option is normally the name of your library. That is, the value | ||
the _name_ property in _package.json_. The generated Error object will | ||
have two properties to define the package: _package_, a string that is | ||
the name of the package, and also a boolean, the name of the package itself. | ||
This lets you check for the type of error easily: | ||
```JavaScript | ||
var error = require('eraro')({package:'mylib'}) | ||
### Support | ||
var err0 = error('code0') | ||
"mylib" === err0.package // true | ||
err0.mylib // true | ||
``` | ||
## Error details | ||
You can supply additional contextual details for debugging or other | ||
purposes. These are placed inside the _details_ property of the | ||
generated Error: | ||
```JavaScript | ||
var error = require('eraro')({package:'mylib'}) | ||
var err0 = error('code0',{foo:'FOO',bar:'BAR'}) | ||
"FOO" === err0.details.foo | ||
"BAR" === err0.details.bar | ||
``` | ||
## Error codes and message templates | ||
To provide consistent error messages to your users, you can define a set of message templates, keyed by code: | ||
```JavaScript | ||
var error = require('eraro')({package:'mylib',msgmap:{ | ||
code0: "The first error, foo is <%=foo%>.", | ||
code1: "The second error, bar is <%=bar%>.", | ||
}}) | ||
``` | ||
When you specify a code, and details, these are inserted into the message (if any) associated with that code: | ||
```JavaScript | ||
var err0 = error('code0',{foo:'FOO',bar:'BAR'}) | ||
"mylib: The first error, foo is FOO." === err0.message | ||
``` | ||
The message templates are [underscorejs templates](http://underscorejs.org/#template) | ||
with the default settings. | ||
If you specify a message directly, this is also interpreted as a template: | ||
```JavaScript | ||
var err0 = error('code2', | ||
'My custom message, details: <%=util.inspect(zed)%>', | ||
{zed:{a:1,b:2}}) | ||
"mylib: My custom message, details: { a: 1, b: 2 }" === err0.message | ||
``` | ||
# The returned Error object | ||
The returned Error object has the following additional properties: | ||
* _code_: String; the code string | ||
* _package_: String; the package name | ||
* _**package-name**_: Boolean (true); a convenience marker for the package | ||
* _msg_: String; the generated message, may differ from original exception message (if any) | ||
* _details_: Object; contextual details of error | ||
* _callpoint_: String; first line of stacktrace that is external to eraro and calling module | ||
You can pass in an existing Error object. The additional properties | ||
will be added to it, but the original message will be used as the | ||
message template, overriding any matching code message. | ||
# In the Wild | ||
For real-world usage examples, see: | ||
* _[use-plugin](http://github.com/rjrodger/use-plugin)_: a utility for providing a plugin interface for extensions to your module | ||
# Support | ||
If you're using this module, feel free to contact me on twitter if you have any questions! :) [@rjrodger](http://twitter.com/rjrodger) | ||
Current Version: 0.1.3 | ||
Current Version: 0.1.4 | ||
@@ -56,4 +159,2 @@ Tested on: node 0.10.26 | ||
...more docs to follow... | ||
[Annotated Source](http://rjrodger.github.io/eraro/doc/eraro.html) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
11516
152
159
1
3
3
2
+ Addedunderscore@~1.6.0
+ Addedunderscore@1.6.0(transitive)