diff --git a/.eslintrc.js b/.eslintrc.js index 77c41af..a292b66 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -10,6 +10,8 @@ module.exports = { strict: ['error', 'safe'], 'no-underscore-dangle': 'off', 'no-param-reassign': 'off', - 'no-unused-vars': ['error', { 'argsIgnorePattern': 'next|res|req|err' }] + 'no-unused-vars': ['error', { 'argsIgnorePattern': 'next|res|req|err' }], + 'func-names': ['error', 'always', { generators: 'never' }], + 'require-yield': 'off' } }; diff --git a/index.js b/index.js index e7e7194..3d2a9fd 100644 --- a/index.js +++ b/index.js @@ -1,10 +1,15 @@ 'use strict'; const { IncomingMessage, ServerResponse } = require('http'); +const co = require('co'); const Layer = require('express/lib/router/layer'); const { Router } = require('express'); +// eslint-disable-next-line func-names, no-empty-function +const GeneratorFunction = (function* () {}).constructor; + const isError = (arg) => arg instanceof Error; +const isGeneratorFunction = (arg) => arg instanceof GeneratorFunction; const isRequest = (arg) => arg instanceof IncomingMessage; const isResponse = (arg) => arg instanceof ServerResponse; const noop = () => {}; @@ -18,6 +23,7 @@ function copyProps(source, dest) { function wrap(fn) { const newFn = function newFn(...args) { + if (isGeneratorFunction(fn)) fn = co.wrap(fn); const ret = fn.apply(this, args); const predicates = [isError, isRequest, isResponse]; const next = args.find((arg) => predicates.every((match) => !match(arg))) || noop; diff --git a/package.json b/package.json index 8e2f26f..f02dddd 100644 --- a/package.json +++ b/package.json @@ -48,5 +48,8 @@ }, "peerDependencies": { "express": "^4.16.2" + }, + "dependencies": { + "co": "^4.6.0" } } diff --git a/test.js b/test.js index 0871e49..8dc9fff 100644 --- a/test.js +++ b/test.js @@ -10,80 +10,160 @@ const assert = require('assert'); const last = (arr) => arr[arr.length - 1]; describe('express-async-errors', () => { - it('should propagate route handler errors to error handler', () => { - const app = express(); + describe('supports async', () => { + it('should propagate route handler errors to error handler', () => { + const app = express(); - app.get('/test', async () => { - throw new Error('error'); - }); + app.get('/test', async () => { + throw new Error('error'); + }); - app.use((err, req, res, next) => { - res.status(495).end(); + app.use((err, req, res, next) => { + res.status(495).end(); + }); + + return supertest(app) + .get('/test') + .expect(495); }); - return supertest(app) - .get('/test') - .expect(495); - }); + it('should propagate middleware errors to error handler', () => { + const app = express(); - it('should propagate middleware errors to error handler', () => { - const app = express(); + app.use(async () => { + throw new Error('error'); + }); - app.use(async () => { - throw new Error('error'); - }); + app.get('/test', async () => { + throw new Error('error'); + }); - app.get('/test', async () => { - throw new Error('error'); - }); + app.use((err, req, res, next) => { + res.status(495).end(); + }); - app.use((err, req, res, next) => { - res.status(495).end(); + return supertest(app) + .get('/test') + .expect(495); }); - return supertest(app) - .get('/test') - .expect(495); - }); + it('should propagate error middleware errors to error handler', () => { + const app = express(); - it('should propagate error middleware errors to error handler', () => { - const app = express(); + app.get('/test', async () => { + throw new Error('error'); + }); - app.get('/test', async () => { - throw new Error('error'); - }); + app.use(async (err, req, res, next) => { + throw new Error('error'); + }); - app.use(async (err, req, res, next) => { - throw new Error('error'); - }); + app.use((err, req, res, next) => { + res.status(495).end(); + }); - app.use((err, req, res, next) => { - res.status(495).end(); + return supertest(app) + .get('/test') + .expect(495); }); - return supertest(app) - .get('/test') - .expect(495); + it('should propagate param middleware errors to error handler', () => { + const app = express(); + + app.param('id', async () => { + throw new Error('error'); + }); + + app.get('/test/:id', async (err, req, next, id) => { + throw new Error(`error ${id}`); + }); + + app.use((err, req, res, next) => { + res.status(495).end(); + }); + + return supertest(app) + .get('/test/12') + .expect(495); + }); }); - it('should propagate param middleware errors to error handler', () => { - const app = express(); + describe('supports generators', () => { + it('should propagate route handler errors to error handler', () => { + const app = express(); + + app.get('/test', function* () { + throw new Error('error'); + }); - app.param('id', async () => { - throw new Error('error'); + app.use((err, req, res, next) => { + res.status(495).end(); + }); + + return supertest(app) + .get('/test') + .expect(495); }); - app.get('/test/:id', async (err, req, next, id) => { - throw new Error(`error ${id}`); + it('should propagate middleware errors to error handler', () => { + const app = express(); + + app.use(function* () { + throw new Error('error'); + }); + + app.get('/test', function* () { + throw new Error('error'); + }); + + app.use((err, req, res, next) => { + res.status(495).end(); + }); + + return supertest(app) + .get('/test') + .expect(495); }); - app.use((err, req, res, next) => { - res.status(495).end(); + it('should propagate error middleware errors to error handler', () => { + const app = express(); + + app.get('/test', function* () { + throw new Error('error'); + }); + + app.use(async (err, req, res, next) => { + throw new Error('error'); + }); + + app.use((err, req, res, next) => { + res.status(495).end(); + }); + + return supertest(app) + .get('/test') + .expect(495); }); - return supertest(app) - .get('/test/12') - .expect(495); + it('should propagate param middleware errors to error handler', () => { + const app = express(); + + app.param('id', function* () { + throw new Error('error'); + }); + + app.get('/test/:id', function* (err, req, next, id) { + throw new Error(`error ${id}`); + }); + + app.use((err, req, res, next) => { + res.status(495).end(); + }); + + return supertest(app) + .get('/test/12') + .expect(495); + }); }); it('should preserve the router stack for external routes', () => { diff --git a/yarn.lock b/yarn.lock index 15ef9bf..513e682 100644 --- a/yarn.lock +++ b/yarn.lock @@ -345,6 +345,11 @@ cliui@^5.0.0: strip-ansi "^5.2.0" wrap-ansi "^5.1.0" +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= + color-convert@^1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.0.tgz#1accf97dd739b983bf994d56fec8f95853641b7a"