From a94b34f37f2c1613b9b5c53b387e03c73a414e0a Mon Sep 17 00:00:00 2001 From: James Allen Date: Wed, 7 Mar 2018 16:42:39 +0000 Subject: [PATCH] Initial commit of stand-alone module --- libraries/o-error/.gitignore | 2 + libraries/o-error/index.js | 53 ++++++++ libraries/o-error/npm-shrinkwrap.json | 142 ++++++++++++++++++++++ libraries/o-error/package.json | 16 +++ libraries/o-error/test/error-type.test.js | 131 ++++++++++++++++++++ libraries/o-error/test/support/index.js | 5 + 6 files changed, 349 insertions(+) create mode 100644 libraries/o-error/.gitignore create mode 100644 libraries/o-error/index.js create mode 100644 libraries/o-error/npm-shrinkwrap.json create mode 100644 libraries/o-error/package.json create mode 100644 libraries/o-error/test/error-type.test.js create mode 100644 libraries/o-error/test/support/index.js diff --git a/libraries/o-error/.gitignore b/libraries/o-error/.gitignore new file mode 100644 index 0000000000..d5700888a3 --- /dev/null +++ b/libraries/o-error/.gitignore @@ -0,0 +1,2 @@ +node_modules/ + diff --git a/libraries/o-error/index.js b/libraries/o-error/index.js new file mode 100644 index 0000000000..690ecf24c8 --- /dev/null +++ b/libraries/o-error/index.js @@ -0,0 +1,53 @@ +'use strict' + +var util = require('util') + +/** + * Make custom error types. There are many, many modules for this, but they all + * seem a bit weird. This approach is based on + * https://gist.github.com/justmoon/15511f92e5216fa2624b + * which seems sensible. This module mainly tries to DRY it up a bit. It also + * incorporates some ideas from the verror package. Maybe it can become its own + * package one day. + * + * TODO: Will this work under browserify? + * + * @module + */ + +function extendErrorType (base, name, builder) { + var errorConstructor = function () { + Error.captureStackTrace && Error.captureStackTrace(this, this.constructor) + if (builder) builder.apply(this, arguments) + this.name = name + } + + util.inherits(errorConstructor, base) + + errorConstructor.prototype.withCause = function (cause) { + this.cause = cause + if (this.message && cause.message) { + this.message += ': ' + cause.message + } + return this + } + + return errorConstructor +} + +function defineErrorType (name, builder) { + return extendErrorType(Error, name, builder) +} + +function extendErrorTypeIn (container, base, name, builder) { + container[name] = extendErrorType(base, name, builder) +} + +function defineErrorTypeIn (container, name, builder) { + extendErrorTypeIn(container, Error, name, builder) +} + +exports.extend = extendErrorType +exports.define = defineErrorType +exports.extendIn = extendErrorTypeIn +exports.defineIn = defineErrorTypeIn diff --git a/libraries/o-error/npm-shrinkwrap.json b/libraries/o-error/npm-shrinkwrap.json new file mode 100644 index 0000000000..972e0a7bd2 --- /dev/null +++ b/libraries/o-error/npm-shrinkwrap.json @@ -0,0 +1,142 @@ +{ + "name": "overleaf-error-type", + "version": "1.0.0", + "dependencies": { + "chai": { + "version": "3.4.1", + "from": "chai@>=3.3.0 <4.0.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-3.4.1.tgz", + "dependencies": { + "assertion-error": { + "version": "1.0.1", + "from": "assertion-error@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.1.tgz" + }, + "deep-eql": { + "version": "0.1.3", + "from": "deep-eql@>=0.1.3 <0.2.0", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", + "dependencies": { + "type-detect": { + "version": "0.1.1", + "from": "type-detect@0.1.1", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz" + } + } + }, + "type-detect": { + "version": "1.0.0", + "from": "type-detect@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz" + } + } + }, + "mocha": { + "version": "2.3.4", + "from": "mocha@>=2.3.3 <3.0.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-2.3.4.tgz", + "dependencies": { + "commander": { + "version": "2.3.0", + "from": "commander@2.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.3.0.tgz" + }, + "debug": { + "version": "2.2.0", + "from": "debug@2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "dependencies": { + "ms": { + "version": "0.7.1", + "from": "ms@0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" + } + } + }, + "diff": { + "version": "1.4.0", + "from": "diff@1.4.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz" + }, + "escape-string-regexp": { + "version": "1.0.2", + "from": "escape-string-regexp@1.0.2", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz" + }, + "glob": { + "version": "3.2.3", + "from": "glob@3.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz", + "dependencies": { + "graceful-fs": { + "version": "2.0.3", + "from": "graceful-fs@>=2.0.0 <2.1.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "0.2.14", + "from": "minimatch@>=0.2.11 <0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "dependencies": { + "lru-cache": { + "version": "2.7.3", + "from": "lru-cache@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz" + }, + "sigmund": { + "version": "1.0.1", + "from": "sigmund@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + } + } + } + }, + "growl": { + "version": "1.8.1", + "from": "growl@1.8.1", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.8.1.tgz" + }, + "jade": { + "version": "0.26.3", + "from": "jade@0.26.3", + "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", + "dependencies": { + "commander": { + "version": "0.6.1", + "from": "commander@0.6.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz" + }, + "mkdirp": { + "version": "0.3.0", + "from": "mkdirp@0.3.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz" + } + } + }, + "mkdirp": { + "version": "0.5.0", + "from": "mkdirp@0.5.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", + "dependencies": { + "minimist": { + "version": "0.0.8", + "from": "minimist@0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } + } + }, + "supports-color": { + "version": "1.2.0", + "from": "supports-color@1.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-1.2.0.tgz" + } + } + } + } +} diff --git a/libraries/o-error/package.json b/libraries/o-error/package.json new file mode 100644 index 0000000000..1ae8693dae --- /dev/null +++ b/libraries/o-error/package.json @@ -0,0 +1,16 @@ +{ + "name": "overleaf-error-type", + "version": "1.0.0", + "description": "Utility for creating custom error types.", + "main": "index.js", + "scripts": { + "test": "mocha --require test/support" + }, + "author": "team@overleaf.com", + "license": "Proprietary", + "private": true, + "devDependencies": { + "chai": "^3.3.0", + "mocha": "^2.3.3" + } +} diff --git a/libraries/o-error/test/error-type.test.js b/libraries/o-error/test/error-type.test.js new file mode 100644 index 0000000000..341d99e8f4 --- /dev/null +++ b/libraries/o-error/test/error-type.test.js @@ -0,0 +1,131 @@ +'use strict' + +var errorType = require('..') + +describe('errorType', function () { + it('defines a custom error type', function () { + var CustomError = errorType.define('CustomError') + + function doSomethingBad () { + throw new CustomError() + } + + try { + doSomethingBad() + expect.fail('should have thrown') + } catch (e) { + // should set the name to the error's name + expect(e.name).to.equal('CustomError') + + // should be an instance of the error type + expect(e instanceof CustomError).to.be.true + + // should be an instance of the built-in Error type + expect(e instanceof Error).to.be.true + + // should be recognised by util.isError + expect(require('util').isError(e)).to.be.true + + // should have a stack trace + expect(e.stack).to.be.truthy + + // toString should return the default error message formatting + expect(e.toString()).to.equal('CustomError') + + // stack should start with the default error message formatting + expect(e.stack.split('\n')[0], 'CustomError') + + // first stack frame should be the function where the error was thrown + expect(e.stack.split('\n')[1]).to.match(/doSomethingBad/) + } + }) + + it('defines a custom error type with a message', function () { + var CustomError = errorType.define('CustomError', function (x) { + this.message = 'x=' + x + this.x = x + }) + + function doSomethingBad () { + throw new CustomError('foo') + } + + try { + doSomethingBad() + expect.fail('should have thrown') + } catch (e) { + expect(e.toString()).to.equal('CustomError: x=foo') + expect(e.message).to.equal('x=foo') + expect(e.x).to.equal('foo') + } + }) + + it('defines extended error type', function () { + var BaseError = errorType.define('BaseError') + var DerivedError = errorType.extend(BaseError, 'DerivedError') + + function doSomethingBad () { + throw new DerivedError() + } + + try { + doSomethingBad() + expect.fail('should have thrown') + } catch (e) { + expect(e.toString()).to.equal('DerivedError') + } + }) + + it('defines error types in a container object', function () { + var SomeClass = {} + errorType.defineIn(SomeClass, 'CustomError') + + function doSomethingBad () { + throw new SomeClass.CustomError() + } + + try { + doSomethingBad() + expect.fail('should have thrown') + } catch (e) { + expect(e.toString()).to.equal('CustomError') + } + }) + + it('extends error types in a container object', function () { + var SomeClass = {} + errorType.defineIn(SomeClass, 'CustomError', function (payload) { + this.message = 'custom error' + this.payload = payload + }) + errorType.extendIn(SomeClass, SomeClass.CustomError, 'DerivedCustomError', + function (payload) { + SomeClass.CustomError.call(this, payload) + this.message = 'derived custom error' + }) + + function doSomethingBad () { + throw new SomeClass.CustomError(123) + } + + try { + doSomethingBad() + expect.fail('should have thrown') + } catch (e) { + expect(e.toString()).to.equal('CustomError: custom error') + expect(e.payload).to.equal(123) + } + + function doSomethingBadWithDerived () { + throw new SomeClass.DerivedCustomError(456) + } + + try { + doSomethingBadWithDerived() + expect.fail('should have thrown') + } catch (e) { + expect(e.toString()).to.equal('DerivedCustomError: derived custom error') + expect(e.payload).to.equal(456) + } + }) +}) diff --git a/libraries/o-error/test/support/index.js b/libraries/o-error/test/support/index.js new file mode 100644 index 0000000000..b93d14b5f6 --- /dev/null +++ b/libraries/o-error/test/support/index.js @@ -0,0 +1,5 @@ +'use strict' + +var chai = require('chai') + +global.expect = chai.expect