-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(fullStack): add ability to get reducer/middleware creation stack…
… traces in dev mode
- Loading branch information
1 parent
c8d8a49
commit 1d8268f
Showing
10 changed files
with
179 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
export default function addCreationStack(fn, what) { | ||
const createdAt = new Error(what + ' created at:') | ||
return function withCause(...args) { | ||
try { | ||
return fn(...args) | ||
} catch (error) { | ||
if (!error.creationStack) error.creationStack = () => createdAt.stack | ||
throw error | ||
} | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export default function fullStack(error, wrapped = error => error.stack) { | ||
let result = wrapped(error) | ||
if (error.creationStack) result += '\nCaused by ' + error.creationStack().substring('Error: '.length) | ||
return result | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,18 @@ | ||
import mapKeys from 'lodash.mapkeys' | ||
import createReducer from './createReducer' | ||
import addCreationStack from './addCreationStack' | ||
|
||
export default function prefixReducer(prefix) { | ||
return reducer => { | ||
if (reducer.actionHandlers instanceof Object) { | ||
return createReducer(reducer.initialState, mapKeys(reducer.actionHandlers, (handler, key) => prefix + key)) | ||
} | ||
return (state, action) => typeof action.type === 'string' && action.type.startsWith(prefix) | ||
let result = (state, action) => typeof action.type === 'string' && action.type.startsWith(prefix) | ||
? reducer(state, {...action, type: action.type.substring(prefix.length)}) | ||
: state | ||
|
||
if (process.env.NODE_ENV !== 'production') result = addCreationStack(result, 'reducer') | ||
return result | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import {assert, expect} from 'chai' | ||
import {createReducer, composeReducers, createMiddleware, composeMiddleware, prefixReducer, fullStack} from '../src' | ||
|
||
describe('addCreationStack', () => { | ||
let origNodeEnv | ||
before(() => { | ||
origNodeEnv = process.env.NODE_ENV | ||
process.env.NODE_ENV = '' | ||
}) | ||
after(() => process.env.NODE_ENV = origNodeEnv) | ||
|
||
describe('createReducer', () => { | ||
it('adds creation stack to errors', () => { | ||
const r = createReducer({hello: () => { throw new Error('test') }}) | ||
try { | ||
r({}, {type: 'hello'}) | ||
assert.fail('expected error to be thrown') | ||
} catch (error) { | ||
expect(fullStack(error)).to.match(/caused by reducer created at/i) | ||
} | ||
}) | ||
}) | ||
describe('composeReducers', () => { | ||
it('adds creation stack to errors', () => { | ||
const r = composeReducers(() => { throw new Error('test') }) | ||
try { | ||
r({}, {type: 'hello'}) | ||
assert.fail('expected error to be thrown') | ||
} catch (error) { | ||
expect(fullStack(error)).to.match(/caused by reducer created at/i) | ||
} | ||
}) | ||
}) | ||
describe('createMiddleware', () => { | ||
it('adds creation stack to errors', () => { | ||
const r = createMiddleware({hello: store => next => action => { throw new Error('test') }}) | ||
try { | ||
r(null)(null)({type: 'hello'}) | ||
assert.fail('expected error to be thrown') | ||
} catch (error) { | ||
expect(fullStack(error)).to.match(/caused by middleware created at/i) | ||
} | ||
}) | ||
}) | ||
describe('composeMiddleware', () => { | ||
it('adds creation stack to errors', () => { | ||
const r = composeMiddleware( | ||
store => dispatch => dispatch, | ||
store => next => action => { throw new Error('test') } | ||
) | ||
try { | ||
r(null)(null)({type: 'hello'}) | ||
assert.fail('expected error to be thrown') | ||
} catch (error) { | ||
expect(fullStack(error)).to.match(/caused by middleware created at/i) | ||
} | ||
}) | ||
}) | ||
describe('prefixReducer', () => { | ||
it('adds creation stack to errors', () => { | ||
const r = prefixReducer('hello')(() => { throw new Error('test') }) | ||
try { | ||
r({}, {type: 'hello'}) | ||
assert.fail('expected error to be thrown') | ||
} catch (error) { | ||
expect(fullStack(error)).to.match(/caused by reducer created at/i) | ||
} | ||
}) | ||
}) | ||
}) | ||
|