mirror of
https://github.com/overleaf/overleaf.git
synced 2025-04-20 16:25:43 +00:00
commit
0acf1d338d
13 changed files with 1743 additions and 312 deletions
|
@ -10,8 +10,9 @@ jobs:
|
|||
- node/with-cache:
|
||||
steps:
|
||||
- run: npm install
|
||||
- run: npm run lint
|
||||
- run: npm test
|
||||
workflows:
|
||||
build-and-test:
|
||||
jobs:
|
||||
- build-and-test
|
||||
build-and-test:
|
||||
jobs:
|
||||
- build-and-test
|
||||
|
|
9
libraries/o-error/.editorconfig
Normal file
9
libraries/o-error/.editorconfig
Normal file
|
@ -0,0 +1,9 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
26
libraries/o-error/.eslintrc.json
Normal file
26
libraries/o-error/.eslintrc.json
Normal file
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"extends": [
|
||||
"standard",
|
||||
"plugin:prettier/recommended",
|
||||
"plugin:mocha/recommended",
|
||||
"plugin:chai-expect/recommended",
|
||||
"plugin:chai-friendly/recommended"
|
||||
],
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2018
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["test/**/*.js"],
|
||||
"env": {
|
||||
"mocha": true
|
||||
},
|
||||
"globals": {
|
||||
"expect": "readonly"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
4
libraries/o-error/.prettierrc.json
Normal file
4
libraries/o-error/.prettierrc.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"semi": false,
|
||||
"singleQuote": true
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
[](https://circleci.com/gh/overleaf/o-error)
|
||||
|
||||
Make custom error classes that:
|
||||
|
||||
- pass `instanceof` checks,
|
||||
- have stack traces,
|
||||
- support custom messages and properties (`info`), and
|
||||
|
@ -17,10 +18,10 @@ ES6 classes make it easy to define custom errors by subclassing `Error`. Subclas
|
|||
```js
|
||||
const OError = require('@overleaf/o-error')
|
||||
|
||||
function doSomethingBad () {
|
||||
function doSomethingBad() {
|
||||
throw new OError({
|
||||
message: 'did something bad',
|
||||
info: { thing: 'foo' }
|
||||
info: { thing: 'foo' },
|
||||
})
|
||||
}
|
||||
doSomethingBad()
|
||||
|
@ -35,12 +36,12 @@ doSomethingBad()
|
|||
|
||||
```js
|
||||
class FooError extends OError {
|
||||
constructor (options) {
|
||||
constructor(options) {
|
||||
super({ message: 'failed to foo', ...options })
|
||||
}
|
||||
}
|
||||
|
||||
function doFoo () {
|
||||
function doFoo() {
|
||||
throw new FooError({ info: { foo: 'bar' } })
|
||||
}
|
||||
doFoo()
|
||||
|
@ -54,7 +55,7 @@ doFoo()
|
|||
### Wrapping an inner error (cause)
|
||||
|
||||
```js
|
||||
function doFoo2 () {
|
||||
function doFoo2() {
|
||||
try {
|
||||
throw new Error('bad')
|
||||
} catch (err) {
|
||||
|
|
|
@ -85,5 +85,5 @@ module.exports = {
|
|||
NotAcceptableError,
|
||||
ConflictError,
|
||||
UnprocessableEntityError,
|
||||
TooManyRequestsError
|
||||
TooManyRequestsError,
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ class OError extends Error {
|
|||
* @param {string} message as for built-in Error
|
||||
* @param {?object} info extra data to attach to the error
|
||||
*/
|
||||
constructor ({ message, info }) {
|
||||
constructor({ message, info }) {
|
||||
super(message)
|
||||
this.name = this.constructor.name
|
||||
if (info) {
|
||||
|
@ -35,7 +35,7 @@ class OError extends Error {
|
|||
* @param {Error} cause
|
||||
* @return {this}
|
||||
*/
|
||||
withCause (cause) {
|
||||
withCause(cause) {
|
||||
this.cause = cause
|
||||
if (this.message && cause.message) {
|
||||
this.message += ': ' + cause.message
|
||||
|
@ -53,7 +53,7 @@ class OError extends Error {
|
|||
* @param {?Error} error assumed not to have circular causes
|
||||
* @return {Object}
|
||||
*/
|
||||
function getFullInfo (error) {
|
||||
function getFullInfo(error) {
|
||||
if (!error) return {}
|
||||
const info = getFullInfo(error.cause)
|
||||
if (typeof error.info === 'object') Object.assign(info, error.info)
|
||||
|
@ -67,10 +67,10 @@ function getFullInfo (error) {
|
|||
* @param {?Error} error assumed not to have circular causes
|
||||
* @return {string}
|
||||
*/
|
||||
function getFullStack (error) {
|
||||
function getFullStack(error) {
|
||||
if (!error) return ''
|
||||
const causeStack = getFullStack(error.cause)
|
||||
if (causeStack) return (error.stack + '\ncaused by: ' + causeStack)
|
||||
if (causeStack) return error.stack + '\ncaused by: ' + causeStack
|
||||
return error.stack
|
||||
}
|
||||
|
||||
|
@ -81,7 +81,7 @@ function getFullStack (error) {
|
|||
* @param {function} klass
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function hasCauseInstanceOf (error, klass) {
|
||||
function hasCauseInstanceOf(error, klass) {
|
||||
if (!error) return false
|
||||
return error instanceof klass || hasCauseInstanceOf(error.cause, klass)
|
||||
}
|
||||
|
|
1893
libraries/o-error/package-lock.json
generated
1893
libraries/o-error/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -4,6 +4,7 @@
|
|||
"description": "Make custom error types that pass `instanceof` checks, have stack traces, support custom messages and properties, and can wrap causes (like VError).",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"lint": "eslint .",
|
||||
"test": "mocha --require test/support"
|
||||
},
|
||||
"author": "Overleaf (https://www.overleaf.com)",
|
||||
|
@ -11,6 +12,18 @@
|
|||
"repository": "github:overleaf/o-error",
|
||||
"devDependencies": {
|
||||
"chai": "^3.3.0",
|
||||
"mocha": "^6.1.4"
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-prettier": "^6.10.1",
|
||||
"eslint-config-standard": "^14.1.1",
|
||||
"eslint-plugin-chai-expect": "^2.1.0",
|
||||
"eslint-plugin-chai-friendly": "^0.5.0",
|
||||
"eslint-plugin-import": "^2.20.2",
|
||||
"eslint-plugin-mocha": "^6.3.0",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-prettier": "^3.1.3",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
"eslint-plugin-standard": "^4.0.1",
|
||||
"mocha": "^7.1.1",
|
||||
"prettier": "^2.0.2"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
const OError = require('..')
|
||||
const HttpErrors = require('../http')
|
||||
|
||||
describe('OError/http', () => {
|
||||
it('is instance of OError', () => {
|
||||
describe('OError/http', function () {
|
||||
it('is instance of OError', function () {
|
||||
try {
|
||||
throw new HttpErrors.ConflictError()
|
||||
} catch (e) {
|
||||
|
@ -10,7 +10,7 @@ describe('OError/http', () => {
|
|||
}
|
||||
})
|
||||
|
||||
it('has status code', () => {
|
||||
it('has status code', function () {
|
||||
try {
|
||||
throw new HttpErrors.ConflictError()
|
||||
} catch (e) {
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
const { getFullInfo, getFullStack, hasCauseInstanceOf } = require('..')
|
||||
|
||||
describe('OError.getFullInfo', () => {
|
||||
it('works on a normal error', () => {
|
||||
describe('OError.getFullInfo', function () {
|
||||
it('works on a normal error', function () {
|
||||
const err = new Error('foo')
|
||||
expect(getFullInfo(err)).to.deep.equal({ })
|
||||
expect(getFullInfo(err)).to.deep.equal({})
|
||||
})
|
||||
|
||||
it('works on an error with .info', () => {
|
||||
it('works on an error with .info', function () {
|
||||
const err = new Error('foo')
|
||||
err.info = { userId: 123 }
|
||||
expect(getFullInfo(err)).to.deep.equal({ userId: 123 })
|
||||
})
|
||||
|
||||
it('merges info from a cause chain', () => {
|
||||
it('merges info from a cause chain', function () {
|
||||
const err1 = new Error('foo')
|
||||
const err2 = new Error('bar')
|
||||
err1.cause = err2
|
||||
|
@ -20,14 +20,14 @@ describe('OError.getFullInfo', () => {
|
|||
expect(getFullInfo(err1)).to.deep.equal({ userId: 123 })
|
||||
})
|
||||
|
||||
it('merges info from a cause chain with no info', () => {
|
||||
it('merges info from a cause chain with no info', function () {
|
||||
const err1 = new Error('foo')
|
||||
const err2 = new Error('bar')
|
||||
err1.cause = err2
|
||||
expect(getFullInfo(err1)).to.deep.equal({})
|
||||
})
|
||||
|
||||
it('merges info from a cause chain with duplicate keys', () => {
|
||||
it('merges info from a cause chain with duplicate keys', function () {
|
||||
const err1 = new Error('foo')
|
||||
const err2 = new Error('bar')
|
||||
err1.cause = err2
|
||||
|
@ -36,22 +36,22 @@ describe('OError.getFullInfo', () => {
|
|||
expect(getFullInfo(err1)).to.deep.equal({ userId: 123 })
|
||||
})
|
||||
|
||||
it('works on an error with .info set to a string', () => {
|
||||
it('works on an error with .info set to a string', function () {
|
||||
const err = new Error('foo')
|
||||
err.info = 'test'
|
||||
expect(getFullInfo(err)).to.deep.equal({})
|
||||
})
|
||||
})
|
||||
|
||||
describe('OError.getFullStack', () => {
|
||||
it('works on a normal error', () => {
|
||||
describe('OError.getFullStack', function () {
|
||||
it('works on a normal error', function () {
|
||||
const err = new Error('foo')
|
||||
const fullStack = getFullStack(err)
|
||||
expect(fullStack).to.match(/^Error: foo$/m)
|
||||
expect(fullStack).to.match(/^\s+at /m)
|
||||
})
|
||||
|
||||
it('works on an error with a cause', () => {
|
||||
it('works on an error with a cause', function () {
|
||||
const err1 = new Error('foo')
|
||||
const err2 = new Error('bar')
|
||||
err1.cause = err2
|
||||
|
@ -63,15 +63,15 @@ describe('OError.getFullStack', () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe('OError.hasCauseInstanceOf', () => {
|
||||
it('works on a normal error', () => {
|
||||
describe('OError.hasCauseInstanceOf', function () {
|
||||
it('works on a normal error', function () {
|
||||
const err = new Error('foo')
|
||||
expect(hasCauseInstanceOf(null, Error)).to.be.false
|
||||
expect(hasCauseInstanceOf(err, Error)).to.be.true
|
||||
expect(hasCauseInstanceOf(err, RangeError)).to.be.false
|
||||
})
|
||||
|
||||
it('works on an error with a cause', () => {
|
||||
it('works on an error with a cause', function () {
|
||||
const err1 = new Error('foo')
|
||||
const err2 = new RangeError('bar')
|
||||
err1.cause = err2
|
||||
|
|
|
@ -2,24 +2,24 @@ const OError = require('..')
|
|||
const { expectError } = require('./support')
|
||||
|
||||
class CustomError1 extends OError {
|
||||
constructor (options) {
|
||||
constructor(options) {
|
||||
super({ message: 'failed to foo', ...options })
|
||||
}
|
||||
}
|
||||
|
||||
class CustomError2 extends OError {
|
||||
constructor (options) {
|
||||
constructor(options) {
|
||||
super({ message: 'failed to bar', ...options })
|
||||
}
|
||||
}
|
||||
|
||||
describe('OError', () => {
|
||||
it('handles a custom error type with a cause', () => {
|
||||
function doSomethingBadInternally () {
|
||||
describe('OError', function () {
|
||||
it('handles a custom error type with a cause', function () {
|
||||
function doSomethingBadInternally() {
|
||||
throw new Error('internal error')
|
||||
}
|
||||
|
||||
function doSomethingBad () {
|
||||
function doSomethingBad() {
|
||||
try {
|
||||
doSomethingBadInternally()
|
||||
} catch (err) {
|
||||
|
@ -35,25 +35,23 @@ describe('OError', () => {
|
|||
name: 'CustomError1',
|
||||
klass: CustomError1,
|
||||
message: 'CustomError1: failed to foo: internal error',
|
||||
firstFrameRx: /doSomethingBad/
|
||||
firstFrameRx: /doSomethingBad/,
|
||||
})
|
||||
expect(OError.getFullInfo(e)).to.deep.equal({ userId: 123 })
|
||||
const fullStack = OError.getFullStack(e)
|
||||
expect(fullStack).to.match(
|
||||
/^CustomError1: failed to foo: internal error$/m
|
||||
)
|
||||
expect(fullStack).to.match(
|
||||
/^caused by: Error: internal error$/m
|
||||
)
|
||||
expect(fullStack).to.match(/^caused by: Error: internal error$/m)
|
||||
}
|
||||
})
|
||||
|
||||
it('handles a custom error type with nested causes', () => {
|
||||
function doSomethingBadInternally () {
|
||||
it('handles a custom error type with nested causes', function () {
|
||||
function doSomethingBadInternally() {
|
||||
throw new Error('internal error')
|
||||
}
|
||||
|
||||
function doBar () {
|
||||
function doBar() {
|
||||
try {
|
||||
doSomethingBadInternally()
|
||||
} catch (err) {
|
||||
|
@ -61,7 +59,7 @@ describe('OError', () => {
|
|||
}
|
||||
}
|
||||
|
||||
function doFoo () {
|
||||
function doFoo() {
|
||||
try {
|
||||
doBar()
|
||||
} catch (err) {
|
||||
|
@ -77,11 +75,11 @@ describe('OError', () => {
|
|||
name: 'CustomError1',
|
||||
klass: CustomError1,
|
||||
message: 'CustomError1: failed to foo: failed to bar: internal error',
|
||||
firstFrameRx: /doFoo/
|
||||
firstFrameRx: /doFoo/,
|
||||
})
|
||||
expect(OError.getFullInfo(e)).to.deep.equal({
|
||||
userId: 123,
|
||||
database: 'a'
|
||||
database: 'a',
|
||||
})
|
||||
const fullStack = OError.getFullStack(e)
|
||||
expect(fullStack).to.match(
|
||||
|
@ -90,18 +88,16 @@ describe('OError', () => {
|
|||
expect(fullStack).to.match(
|
||||
/^caused by: CustomError2: failed to bar: internal error$/m
|
||||
)
|
||||
expect(fullStack).to.match(
|
||||
/^caused by: Error: internal error$/m
|
||||
)
|
||||
expect(fullStack).to.match(/^caused by: Error: internal error$/m)
|
||||
}
|
||||
})
|
||||
|
||||
it('handles a custom error without info', () => {
|
||||
it('handles a custom error without info', function () {
|
||||
try {
|
||||
throw new CustomError1({})
|
||||
} catch (e) {
|
||||
expect(OError.getFullInfo(e)).to.deep.equal({})
|
||||
let infoKey = Object.keys(e).find(k => k === 'info')
|
||||
const infoKey = Object.keys(e).find((k) => k === 'info')
|
||||
expect(infoKey).to.not.exist
|
||||
}
|
||||
})
|
||||
|
|
|
@ -4,7 +4,7 @@ var chai = require('chai')
|
|||
|
||||
global.expect = chai.expect
|
||||
|
||||
exports.expectError = function OErrorExpectError (e, expected) {
|
||||
exports.expectError = function OErrorExpectError(e, expected) {
|
||||
// should set the name to the error's name
|
||||
expect(e.name).to.equal(expected.name)
|
||||
|
||||
|
@ -15,7 +15,7 @@ exports.expectError = function OErrorExpectError (e, expected) {
|
|||
expect(e instanceof Error).to.be.true
|
||||
|
||||
// should be recognised by util.isError
|
||||
expect(require('util').isError(e)).to.be.true
|
||||
expect(require('util').types.isNativeError(e)).to.be.true
|
||||
|
||||
// should have a stack trace
|
||||
expect(e.stack).to.be.truthy
|
||||
|
|
Loading…
Add table
Reference in a new issue