diff --git a/.jshintrc b/.jshintrc index c1aba6e..a0a38db 100644 --- a/.jshintrc +++ b/.jshintrc @@ -13,6 +13,7 @@ "immed": true, "browser": true, "es3": true, + "loopfunc": true, "camelcase": true, "nonbsp": true, "freeze": true, diff --git a/.nvmrc b/.nvmrc deleted file mode 100644 index d0e8c69..0000000 --- a/.nvmrc +++ /dev/null @@ -1 +0,0 @@ -0.10.32 diff --git a/.travis.yml b/.travis.yml index b976e10..857558c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,19 @@ language: node_js -node_js: -- '0.12' -script: gulp ci -install: -- npm install sudo: false +node_js: stable addons: sauce_connect: true + firefox: latest + apt: + sources: + - google-chrome + packages: + - google-chrome-stable +before_script: +- npm install +script: +- if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then gulp ci; else gulp pr; fi env: global: - - secure: WrorXhIe0hgRgvaBzNK6PSQEw2VTHKCxqkkNXqIY80eESXETC9nw0WkeALa6Neu06G2K6PxCXuVnaDAAtEarkawiY2OD1jrQEeYm9/2Mnqj8K2f92y57a3uVmUmo4+sqRTj6Ub3q0+/hLhluRWGTNia3kmW8cJKKG6cyWxCwb/o= - - secure: WwBqBCm1q/iIM11n3cBPCwt5X+q62WxlfQGK4KPHlikjUExgLgksc5/ae8TwFwHpL3cL5P4h4I5MfrIK++9vFrn7CICVMkI0LeLTgXCaeUThWP43FsPvG1GrWAJYeXeRk8kc/5gBT+/8x5FxZUunvivIu+gYrqX+Ip7xwfcbGlo= + - secure: VGFNdtgz47pAI9fgXyej0Z10qdcHMjYBgCrHQQLdxDnsEtPqQL77H/TTGgw9xq1wF9BmVM2AS3bCrmHmgZboHaG0QSx813eiNs5ewQvSutrDMpBVqyKcnHvYf3L5WSeOvW8nCcKlh8TTaNSGc27+x0x6UGppI8NfVbZQvjcef5g= + - secure: iEbcv7NrXQtl91xn10xl2z+s76a0Isg5WO6RqCAqeXS9/OWgxgbubLFCPZwfdIMYOpdj7U03iaKL1CzOmT5rFOjM/nxyVu9k7q5WYXwW9HnecVPbXGWEQv6VDDV9QH1Lbh/KtwFKl5YJw7nFTzhCnT6g33aocywwAKL6cwjSQn4= diff --git a/README.md b/README.md index 0a0b4d8..3223de2 100644 --- a/README.md +++ b/README.md @@ -3,14 +3,23 @@ stackframe ## JS Object representation of a stack frame [![Build Status](https://travis-ci.org/stacktracejs/stackframe.svg?branch=master)](https://travis-ci.org/stacktracejs/stackframe) [![Coverage Status](https://img.shields.io/coveralls/stacktracejs/stackframe.svg)](https://coveralls.io/r/stacktracejs/stackframe?branch=master) [![Code Climate](https://codeclimate.com/github/stacktracejs/stackframe/badges/gpa.svg)](https://codeclimate.com/github/stacktracejs/stackframe) -Underlies functionality of other modules within [stacktrace.js](http://www.stacktracejs.com). +Underlies functionality of other modules within [stacktrace.js](https://www.stacktracejs.com). -Written to closely resemble StackFrame representations in [Gecko](http://mxr.mozilla.org/mozilla-central/source/xpcom/base/nsIException.idl#14) and [V8](https://code.google.com/p/v8-wiki/wiki/JavaScriptStackTraceApi) +Written to closely resemble StackFrame representations in [Gecko](http://mxr.mozilla.org/mozilla-central/source/xpcom/base/nsIException.idl#14) and [V8](https://github.com/v8/v8/wiki/Stack%20Trace%20API) ## Usage ```js // Create StackFrame and set properties -var stackFrame = new StackFrame('funName', ['args'], 'http://localhost:3000/file.js', 1, 3288, 'ORIGINAL_STACK_LINE'); +var stackFrame = new StackFrame({ + functionName: 'funName', + args: ['args'], + fileName: 'http://localhost:3000/file.js', + lineNumber: 1, + columnNumber: 3288, + isEval: true, + isNative: false, + source: 'ORIGINAL_STACK_LINE' +}); stackFrame.functionName // => "funName" stackFrame.setFunctionName('newName') @@ -36,9 +45,20 @@ stackFrame.source // => 'ORIGINAL_STACK_LINE' stackFrame.setSource('NEW_SOURCE') stackFrame.getSource() // => 'NEW_SOURCE' +stackFrame.isEval // => true +stackFrame.setIsEval(false) +stackFrame.getIsEval() // => false + +stackFrame.isNative // => false +stackFrame.setIsNative(true) +stackFrame.getIsNative() // => true + stackFrame.toString() // => 'funName(args)@http://localhost:3000/file.js:325:20' ``` +## Browser Support +[![Sauce Test Status](https://saucelabs.com/browser-matrix/stacktracejs.svg)](https://saucelabs.com/u/stacktracejs) + ## Installation ``` npm install stackframe diff --git a/component.json b/component.json index 6363c7e..1b63f8a 100644 --- a/component.json +++ b/component.json @@ -2,7 +2,7 @@ "name": "stackframe", "repository": "stacktracejs/stackframe", "description": "JS Object representation of a stack frame", - "version": "0.3.1", + "version": "1.0.2", "keywords": [ "stacktrace", "error", diff --git a/dist/stackframe.js b/dist/stackframe.js index 9a240f0..35e2c0e 100644 --- a/dist/stackframe.js +++ b/dist/stackframe.js @@ -16,35 +16,33 @@ return !isNaN(parseFloat(n)) && isFinite(n); } - function StackFrame(functionName, args, fileName, lineNumber, columnNumber, source) { - if (functionName !== undefined) { - this.setFunctionName(functionName); - } - if (args !== undefined) { - this.setArgs(args); - } - if (fileName !== undefined) { - this.setFileName(fileName); - } - if (lineNumber !== undefined) { - this.setLineNumber(lineNumber); - } - if (columnNumber !== undefined) { - this.setColumnNumber(columnNumber); - } - if (source !== undefined) { - this.setSource(source); + function _capitalize(str) { + return str[0].toUpperCase() + str.substring(1); + } + + function _getter(p) { + return function () { + return this[p]; + }; + } + + var booleanProps = ['isConstructor', 'isEval', 'isNative', 'isToplevel']; + var numericProps = ['columnNumber', 'lineNumber']; + var stringProps = ['fileName', 'functionName', 'source']; + var arrayProps = ['args']; + + function StackFrame(obj) { + if (obj instanceof Object) { + var props = booleanProps.concat(numericProps.concat(stringProps.concat(arrayProps))); + for (var i = 0; i < props.length; i++) { + if (obj.hasOwnProperty(props[i]) && obj[props[i]] !== undefined) { + this['set' + _capitalize(props[i])](obj[props[i]]); + } + } } } StackFrame.prototype = { - getFunctionName: function () { - return this.functionName; - }, - setFunctionName: function (v) { - this.functionName = String(v); - }, - getArgs: function () { return this.args; }, @@ -55,45 +53,20 @@ this.args = v; }, - // NOTE: Property name may be misleading as it includes the path, - // but it somewhat mirrors V8's JavaScriptStackTraceApi - // https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi and Gecko's - // http://mxr.mozilla.org/mozilla-central/source/xpcom/base/nsIException.idl#14 - getFileName: function () { - return this.fileName; - }, - setFileName: function (v) { - this.fileName = String(v); - }, - - getLineNumber: function () { - return this.lineNumber; - }, - setLineNumber: function (v) { - if (!_isNumber(v)) { - throw new TypeError('Line Number must be a Number'); - } - this.lineNumber = Number(v); - }, - - getColumnNumber: function () { - return this.columnNumber; + getEvalOrigin: function () { + return this.evalOrigin; }, - setColumnNumber: function (v) { - if (!_isNumber(v)) { - throw new TypeError('Column Number must be a Number'); + setEvalOrigin: function (v) { + if (v instanceof StackFrame) { + this.evalOrigin = v; + } else if (v instanceof Object) { + this.evalOrigin = new StackFrame(v); + } else { + throw new TypeError('Eval Origin must be an Object or StackFrame'); } - this.columnNumber = Number(v); }, - getSource: function () { - return this.source; - }, - setSource: function (v) { - this.source = String(v); - }, - - toString: function() { + toString: function () { var functionName = this.getFunctionName() || '{anonymous}'; var args = '(' + (this.getArgs() || []).join(',') + ')'; var fileName = this.getFileName() ? ('@' + this.getFileName()) : ''; @@ -103,5 +76,35 @@ } }; + for (var i = 0; i < booleanProps.length; i++) { + StackFrame.prototype['get' + _capitalize(booleanProps[i])] = _getter(booleanProps[i]); + StackFrame.prototype['set' + _capitalize(booleanProps[i])] = (function (p) { + return function (v) { + this[p] = Boolean(v); + }; + })(booleanProps[i]); + } + + for (var j = 0; j < numericProps.length; j++) { + StackFrame.prototype['get' + _capitalize(numericProps[j])] = _getter(numericProps[j]); + StackFrame.prototype['set' + _capitalize(numericProps[j])] = (function (p) { + return function (v) { + if (!_isNumber(v)) { + throw new TypeError(p + ' must be a Number'); + } + this[p] = Number(v); + }; + })(numericProps[j]); + } + + for (var k = 0; k < stringProps.length; k++) { + StackFrame.prototype['get' + _capitalize(stringProps[k])] = _getter(stringProps[k]); + StackFrame.prototype['set' + _capitalize(stringProps[k])] = (function (p) { + return function (v) { + this[p] = String(v); + }; + })(stringProps[k]); + } + return StackFrame; })); diff --git a/dist/stackframe.min.js b/dist/stackframe.min.js index 2deae4d..41429d0 100644 --- a/dist/stackframe.min.js +++ b/dist/stackframe.min.js @@ -1,2 +1,2 @@ -!function(e,t){"use strict";"function"==typeof define&&define.amd?define("stackframe",[],t):"object"==typeof exports?module.exports=t():e.StackFrame=t()}(this,function(){"use strict";function e(e){return!isNaN(parseFloat(e))&&isFinite(e)}function t(e,t,i,n,r,u){void 0!==e&&this.setFunctionName(e),void 0!==t&&this.setArgs(t),void 0!==i&&this.setFileName(i),void 0!==n&&this.setLineNumber(n),void 0!==r&&this.setColumnNumber(r),void 0!==u&&this.setSource(u)}return t.prototype={getFunctionName:function(){return this.functionName},setFunctionName:function(e){this.functionName=String(e)},getArgs:function(){return this.args},setArgs:function(e){if("[object Array]"!==Object.prototype.toString.call(e))throw new TypeError("Args must be an Array");this.args=e},getFileName:function(){return this.fileName},setFileName:function(e){this.fileName=String(e)},getLineNumber:function(){return this.lineNumber},setLineNumber:function(t){if(!e(t))throw new TypeError("Line Number must be a Number");this.lineNumber=Number(t)},getColumnNumber:function(){return this.columnNumber},setColumnNumber:function(t){if(!e(t))throw new TypeError("Column Number must be a Number");this.columnNumber=Number(t)},getSource:function(){return this.source},setSource:function(e){this.source=String(e)},toString:function(){var t=this.getFunctionName()||"{anonymous}",i="("+(this.getArgs()||[]).join(",")+")",n=this.getFileName()?"@"+this.getFileName():"",r=e(this.getLineNumber())?":"+this.getLineNumber():"",u=e(this.getColumnNumber())?":"+this.getColumnNumber():"";return t+i+n+r+u}},t}); -//# sourceMappingURL=stackframe.min.js.map \ No newline at end of file +!function(t,e){"use strict";"function"==typeof define&&define.amd?define("stackframe",[],e):"object"==typeof exports?module.exports=e():t.StackFrame=e()}(this,function(){"use strict";function t(t){return!isNaN(parseFloat(t))&&isFinite(t)}function e(t){return t[0].toUpperCase()+t.substring(1)}function n(t){return function(){return this[t]}}function r(t){if(t instanceof Object)for(var n=i.concat(o.concat(s.concat(u))),r=0;r (https://github.com/victor-homyakov)", "Oliver Salzburg (https://github.com/oliversalzburg)" ], - "version": "0.3.1", - "license": "SEE LICENSE IN LICENSE", + "version": "1.0.2", + "license": "Unlicense", "keywords": [ "stacktrace", "error", "debugger", "stack frame" ], - "homepage": "http://www.stacktracejs.com", + "homepage": "https://www.stacktracejs.com", "repository": { "type": "git", "url": "git://github.com/stacktracejs/stackframe.git" }, "devDependencies": { "colors": "^1.1.2", - "del": "^1.2.0", + "del": "^2.2.0", "gulp": "^3.9.0", "gulp-coveralls": "^0.1.4", - "gulp-jshint": "^1.11.2", + "gulp-jshint": "^2.0.0", "gulp-rename": "^1.2.2", - "gulp-sourcemaps": "^1.5.2", - "gulp-uglify": "^1.2.0", - "jasmine-node": "~1.14", - "karma": "~0.12", - "karma-chrome-launcher": "^0.1.5", - "karma-coverage": "^0.2.6", - "karma-firefox-launcher": "^0.1.3", - "karma-ie-launcher": "^0.1.5", - "karma-jasmine": "^0.2.3", - "karma-opera-launcher": "^0.1.0", - "karma-phantomjs2-launcher": "^0.3.0", + "gulp-sourcemaps": "^1.6.0", + "gulp-uglify": "^1.5.1", + "jasmine": "^2.4.1", + "jasmine-core": "^2.4.1", + "jshint": "^2.8.0", + "karma": "^0.13.15", + "karma-chrome-launcher": "^0.2.2", + "karma-coverage": "^0.5.3", + "karma-firefox-launcher": "^0.1.7", + "karma-ie-launcher": "^0.2.0", + "karma-jasmine": "^0.3.6", + "karma-opera-launcher": "^0.3.0", + "karma-phantomjs2-launcher": "^0.3.2", "karma-safari-launcher": "^0.1.1", - "karma-sauce-launcher": "^0.2.10", - "run-sequence": "^1.1.2" + "karma-sauce-launcher": "^0.3.0", + "karma-spec-reporter": "0.0.23", + "run-sequence": "^1.1.5" }, "bugs": { "url": "https://github.com/stacktracejs/stackframe/issues" diff --git a/spec/stackframe-spec.js b/spec/stackframe-spec.js index b3f3ae1..81ee17b 100644 --- a/spec/stackframe-spec.js +++ b/spec/stackframe-spec.js @@ -8,7 +8,7 @@ describe('StackFrame', function () { it('throws an error given an illogical line number', function() { var fn = function () { - new StackFrame('foo', [], 'path/to/file.js', 'BOGUS'); + new StackFrame({lineNumber: 'BOGUS'}); }; expect(fn).toThrow(); }); @@ -38,6 +38,26 @@ describe('StackFrame', function () { }); }); + describe('#setEvalOrigin', function() { + var unit = new StackFrame(); + + it('throws an error given a non-Object', function() { + expect(function() { + unit.setEvalOrigin('BOGUS'); + }).toThrow(new TypeError('Eval Origin must be an Object or StackFrame')); + }); + + it('handles given StackFrame', function() { + unit.setEvalOrigin(new StackFrame({lineNumber: 2})); + expect(unit.getEvalOrigin().getLineNumber()).toEqual(2); + }); + + it('handles given Object', function() { + unit.setEvalOrigin({functionName: 'evalFn'}); + expect(unit.getEvalOrigin().getFunctionName()).toEqual('evalFn'); + }); + }); + describe('#setFileName', function() { var unit = new StackFrame(); it('coerces input to String', function() { @@ -57,7 +77,7 @@ describe('StackFrame', function () { }); it('throws an error given input that cannot be coerced', function() { - expect(function() { unit.setLineNumber('BOGUS'); }).toThrow(new TypeError('Line Number must be a Number')); + expect(function() { unit.setLineNumber('BOGUS'); }).toThrow(new TypeError('lineNumber must be a Number')); }); }); @@ -69,7 +89,42 @@ describe('StackFrame', function () { }); it('throws an error given input that cannot be coerced', function() { - expect(function() { unit.setColumnNumber('BOGUS'); }).toThrow(new TypeError('Column Number must be a Number')); + expect(function() { unit.setColumnNumber('BOGUS'); }).toThrow(new TypeError('columnNumber must be a Number')); + }); + }); + + describe('#setIsEval', function() { + var unit = new StackFrame(); + it('coerces input to Boolean', function() { + unit.setIsEval('true'); + expect(unit.getIsEval()).toBe(true); + }); + }); + + describe('#setIsConstructor', function() { + var unit = new StackFrame(); + it('coerces input to Boolean', function() { + unit.setIsConstructor(0); + expect(unit.getIsConstructor()).toBe(false); + expect(unit.isConstructor).toBe(false); + }); + }); + + describe('#setIsNative', function() { + var unit = new StackFrame(); + it('coerces input to Boolean', function() { + unit.setIsNative(undefined); + expect(unit.getIsNative()).toBe(false); + expect(unit.isNative).toBe(false); + }); + }); + + describe('#setIsToplevel', function() { + var unit = new StackFrame(); + it('coerces input to Boolean', function() { + unit.setIsToplevel(null); + expect(unit.getIsToplevel()).toBe(false); + expect(unit.isToplevel).toBe(false); }); }); @@ -86,7 +141,16 @@ describe('StackFrame', function () { expect(new StackFrame().toString()).toEqual('{anonymous}()'); }); it('represents complete StackFrame same as old stacktrace.js', function() { - var unit = new StackFrame('fun', [1, 2], 'http://site.com/path.js', 1, 4567, 'SOURCE'); + var unit = new StackFrame({ + functionName: 'fun', + args: [1, 2], + fileName: 'http://site.com/path.js', + lineNumber: 1, + columnNumber: 4567, + isEval: false, + isNative: false, + source: 'SOURCE' + }); expect(unit.toString()).toEqual('fun(1,2)@http://site.com/path.js:1:4567'); }); }); diff --git a/stackframe.js b/stackframe.js index 9a240f0..b54524d 100644 --- a/stackframe.js +++ b/stackframe.js @@ -16,35 +16,34 @@ return !isNaN(parseFloat(n)) && isFinite(n); } - function StackFrame(functionName, args, fileName, lineNumber, columnNumber, source) { - if (functionName !== undefined) { - this.setFunctionName(functionName); - } - if (args !== undefined) { - this.setArgs(args); - } - if (fileName !== undefined) { - this.setFileName(fileName); - } - if (lineNumber !== undefined) { - this.setLineNumber(lineNumber); - } - if (columnNumber !== undefined) { - this.setColumnNumber(columnNumber); - } - if (source !== undefined) { - this.setSource(source); + function _capitalize(str) { + return str[0].toUpperCase() + str.substring(1); + } + + function _getter(p) { + return function () { + return this[p]; + }; + } + + var booleanProps = ['isConstructor', 'isEval', 'isNative', 'isToplevel']; + var numericProps = ['columnNumber', 'lineNumber']; + var stringProps = ['fileName', 'functionName', 'source']; + var arrayProps = ['args']; + + var props = booleanProps.concat(numericProps.concat(stringProps.concat(arrayProps))); + + function StackFrame(obj) { + if (obj instanceof Object) { + for (var i = 0; i < props.length; i++) { + if (obj.hasOwnProperty(props[i]) && obj[props[i]] !== undefined) { + this['set' + _capitalize(props[i])](obj[props[i]]); + } + } } } StackFrame.prototype = { - getFunctionName: function () { - return this.functionName; - }, - setFunctionName: function (v) { - this.functionName = String(v); - }, - getArgs: function () { return this.args; }, @@ -55,45 +54,20 @@ this.args = v; }, - // NOTE: Property name may be misleading as it includes the path, - // but it somewhat mirrors V8's JavaScriptStackTraceApi - // https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi and Gecko's - // http://mxr.mozilla.org/mozilla-central/source/xpcom/base/nsIException.idl#14 - getFileName: function () { - return this.fileName; - }, - setFileName: function (v) { - this.fileName = String(v); - }, - - getLineNumber: function () { - return this.lineNumber; - }, - setLineNumber: function (v) { - if (!_isNumber(v)) { - throw new TypeError('Line Number must be a Number'); - } - this.lineNumber = Number(v); - }, - - getColumnNumber: function () { - return this.columnNumber; + getEvalOrigin: function () { + return this.evalOrigin; }, - setColumnNumber: function (v) { - if (!_isNumber(v)) { - throw new TypeError('Column Number must be a Number'); + setEvalOrigin: function (v) { + if (v instanceof StackFrame) { + this.evalOrigin = v; + } else if (v instanceof Object) { + this.evalOrigin = new StackFrame(v); + } else { + throw new TypeError('Eval Origin must be an Object or StackFrame'); } - this.columnNumber = Number(v); }, - getSource: function () { - return this.source; - }, - setSource: function (v) { - this.source = String(v); - }, - - toString: function() { + toString: function () { var functionName = this.getFunctionName() || '{anonymous}'; var args = '(' + (this.getArgs() || []).join(',') + ')'; var fileName = this.getFileName() ? ('@' + this.getFileName()) : ''; @@ -103,5 +77,35 @@ } }; + for (var i = 0; i < booleanProps.length; i++) { + StackFrame.prototype['get' + _capitalize(booleanProps[i])] = _getter(booleanProps[i]); + StackFrame.prototype['set' + _capitalize(booleanProps[i])] = (function (p) { + return function (v) { + this[p] = Boolean(v); + }; + })(booleanProps[i]); + } + + for (var j = 0; j < numericProps.length; j++) { + StackFrame.prototype['get' + _capitalize(numericProps[j])] = _getter(numericProps[j]); + StackFrame.prototype['set' + _capitalize(numericProps[j])] = (function (p) { + return function (v) { + if (!_isNumber(v)) { + throw new TypeError(p + ' must be a Number'); + } + this[p] = Number(v); + }; + })(numericProps[j]); + } + + for (var k = 0; k < stringProps.length; k++) { + StackFrame.prototype['get' + _capitalize(stringProps[k])] = _getter(stringProps[k]); + StackFrame.prototype['set' + _capitalize(stringProps[k])] = (function (p) { + return function (v) { + this[p] = String(v); + }; + })(stringProps[k]); + } + return StackFrame; }));