mirror of
				https://github.com/reduxjs/redux-devtools.git
				synced 2025-10-31 16:07:45 +03:00 
			
		
		
		
	Limit stack trace frames
This commit is contained in:
		
							parent
							
								
									6efcc788cf
								
							
						
					
					
						commit
						faea737868
					
				|  | @ -51,6 +51,7 @@ export default function configureStore(initialState) { | ||||||
|     - **shouldStartLocked** *boolean* - if specified as `true`, it will not allow any non-monitor actions to be dispatched till `lockChanges(false)` is dispatched. Default is `false`. |     - **shouldStartLocked** *boolean* - if specified as `true`, it will not allow any non-monitor actions to be dispatched till `lockChanges(false)` is dispatched. Default is `false`. | ||||||
|     - **shouldHotReload** *boolean* - if set to `false`, will not recompute the states on hot reloading (or on replacing the reducers). Default to `true`. |     - **shouldHotReload** *boolean* - if set to `false`, will not recompute the states on hot reloading (or on replacing the reducers). Default to `true`. | ||||||
|     - **trace** *boolean* or *function* - if set to `true`, will include stack trace for every dispatched action. You can use a function (with action object as argument) which should return `new Error().stack` string, getting the stack outside of reducers. Default to `false`. |     - **trace** *boolean* or *function* - if set to `true`, will include stack trace for every dispatched action. You can use a function (with action object as argument) which should return `new Error().stack` string, getting the stack outside of reducers. Default to `false`. | ||||||
|  |     - **traceLimit** *number* - maximum stack trace frames to be stored (in case `trace` option was provided as `true`). By default it's `10`. Note that for Chrome there's a global limit to `10`, so you should also override the global `Error.stackTraceLimit` for more. If `trace` option is a function, `traceLimit` will have no effect, that should be handled there like so: `trace: () => new Error().stack.split('\n').slice(0, limit+1).join('\n')`. There's `+1` for `Error\n`. | ||||||
| 
 | 
 | ||||||
| ### License | ### License | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -23,7 +23,7 @@ export const ActionTypes = { | ||||||
|  * Action creators to change the History state. |  * Action creators to change the History state. | ||||||
|  */ |  */ | ||||||
| export const ActionCreators = { | export const ActionCreators = { | ||||||
|   performAction(action, trace, toExcludeFromTrace) { |   performAction(action, trace, traceLimit, toExcludeFromTrace) { | ||||||
|     if (!isPlainObject(action)) { |     if (!isPlainObject(action)) { | ||||||
|       throw new Error( |       throw new Error( | ||||||
|         'Actions must be plain objects. ' + |         'Actions must be plain objects. ' + | ||||||
|  | @ -40,6 +40,7 @@ export const ActionCreators = { | ||||||
| 
 | 
 | ||||||
|     let stack; |     let stack; | ||||||
|     let error; |     let error; | ||||||
|  |     let frames; | ||||||
|     if (trace) { |     if (trace) { | ||||||
|       if (typeof trace === 'function') stack = trace(action); |       if (typeof trace === 'function') stack = trace(action); | ||||||
|       else { |       else { | ||||||
|  | @ -47,6 +48,10 @@ export const ActionCreators = { | ||||||
|         // https://v8.dev/docs/stack-trace-api#stack-trace-collection-for-custom-exceptions
 |         // https://v8.dev/docs/stack-trace-api#stack-trace-collection-for-custom-exceptions
 | ||||||
|         if (Error.captureStackTrace) Error.captureStackTrace(error, toExcludeFromTrace); |         if (Error.captureStackTrace) Error.captureStackTrace(error, toExcludeFromTrace); | ||||||
|         stack = error.stack; |         stack = error.stack; | ||||||
|  |         if (typeof Error.stackTraceLimit !== 'number' || Error.stackTraceLimit > traceLimit) { | ||||||
|  |           frames = stack.split('\n'); | ||||||
|  |           if (frames.length > traceLimit) stack = frames.slice(0, traceLimit + 1).join('\n'); // +1 for `Error\n`
 | ||||||
|  |         } | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -197,8 +202,8 @@ function recomputeStates( | ||||||
| /** | /** | ||||||
|  * Lifts an app's action into an action on the lifted store. |  * Lifts an app's action into an action on the lifted store. | ||||||
|  */ |  */ | ||||||
| export function liftAction(action, trace, toExcludeFromTrace) { | export function liftAction(action, trace, traceLimit, toExcludeFromTrace) { | ||||||
|   return ActionCreators.performAction(action, trace, toExcludeFromTrace); |   return ActionCreators.performAction(action, trace, traceLimit, toExcludeFromTrace); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | @ -605,6 +610,7 @@ export function unliftState(liftedState) { | ||||||
| export function unliftStore(liftedStore, liftReducer, options) { | export function unliftStore(liftedStore, liftReducer, options) { | ||||||
|   let lastDefinedState; |   let lastDefinedState; | ||||||
|   const trace = options.trace || options.shouldIncludeCallstack; |   const trace = options.trace || options.shouldIncludeCallstack; | ||||||
|  |   const traceLimit = options.traceLimit || 10; | ||||||
| 
 | 
 | ||||||
|   function getState() { |   function getState() { | ||||||
|     const state = unliftState(liftedStore.getState()); |     const state = unliftState(liftedStore.getState()); | ||||||
|  | @ -620,7 +626,7 @@ export function unliftStore(liftedStore, liftReducer, options) { | ||||||
|     liftedStore, |     liftedStore, | ||||||
| 
 | 
 | ||||||
|     dispatch(action) { |     dispatch(action) { | ||||||
|       liftedStore.dispatch(liftAction(action, trace, this.dispatch)); |       liftedStore.dispatch(liftAction(action, trace, traceLimit, this.dispatch)); | ||||||
|       return action; |       return action; | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -713,6 +713,79 @@ describe('instrument', () => { | ||||||
|       expect(exportedState.actionsById[1].stack).toNotMatch(/instrument.js/); |       expect(exportedState.actionsById[1].stack).toNotMatch(/instrument.js/); | ||||||
|       expect(exportedState.actionsById[1].stack).toContain('instrument.spec.js'); |       expect(exportedState.actionsById[1].stack).toContain('instrument.spec.js'); | ||||||
|       expect(exportedState.actionsById[1].stack).toContain('/mocha/'); |       expect(exportedState.actionsById[1].stack).toContain('/mocha/'); | ||||||
|  |       expect(exportedState.actionsById[1].stack.split('\n').length).toBe(10 + 1); // +1 is for `Error\n`
 | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('should include only 3 frames for stack trace', () => { | ||||||
|  |       function fn1() { | ||||||
|  |         monitoredStore = createStore(counter, instrument(undefined, { trace: true, traceLimit: 3 })); | ||||||
|  |         monitoredLiftedStore = monitoredStore.liftedStore; | ||||||
|  |         monitoredStore.dispatch({ type: 'INCREMENT' }); | ||||||
|  | 
 | ||||||
|  |         exportedState = monitoredLiftedStore.getState(); | ||||||
|  |         expect(exportedState.actionsById[0].stack).toBe(undefined); | ||||||
|  |         expect(exportedState.actionsById[1].stack).toBeA('string'); | ||||||
|  |         expect(exportedState.actionsById[1].stack).toMatch(/at fn1 /); | ||||||
|  |         expect(exportedState.actionsById[1].stack).toMatch(/at fn2 /); | ||||||
|  |         expect(exportedState.actionsById[1].stack).toMatch(/at fn3 /); | ||||||
|  |         expect(exportedState.actionsById[1].stack).toNotMatch(/at fn4 /); | ||||||
|  |         expect(exportedState.actionsById[1].stack).toContain('instrument.spec.js'); | ||||||
|  |         expect(exportedState.actionsById[1].stack.split('\n').length).toBe(3 + 1); | ||||||
|  |       } | ||||||
|  |       function fn2() { return fn1(); } | ||||||
|  |       function fn3() { return fn2(); } | ||||||
|  |       function fn4() { return fn3(); } | ||||||
|  |       fn4(); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('should include only 3 frames for stack trace when Error.stackTraceLimit is 3', () => { | ||||||
|  |       const stackTraceLimit = Error.stackTraceLimit; | ||||||
|  |       Error.stackTraceLimit = 3; | ||||||
|  |       function fn1() { | ||||||
|  |         monitoredStore = createStore(counter, instrument(undefined, { trace: true })); | ||||||
|  |         monitoredLiftedStore = monitoredStore.liftedStore; | ||||||
|  |         monitoredStore.dispatch({ type: 'INCREMENT' }); | ||||||
|  | 
 | ||||||
|  |         exportedState = monitoredLiftedStore.getState(); | ||||||
|  |         expect(exportedState.actionsById[0].stack).toBe(undefined); | ||||||
|  |         expect(exportedState.actionsById[1].stack).toBeA('string'); | ||||||
|  |         expect(exportedState.actionsById[1].stack).toMatch(/at fn1 /); | ||||||
|  |         expect(exportedState.actionsById[1].stack).toMatch(/at fn2 /); | ||||||
|  |         expect(exportedState.actionsById[1].stack).toMatch(/at fn3 /); | ||||||
|  |         expect(exportedState.actionsById[1].stack).toNotMatch(/at fn4 /); | ||||||
|  |         expect(exportedState.actionsById[1].stack).toContain('instrument.spec.js'); | ||||||
|  |         expect(exportedState.actionsById[1].stack.split('\n').length).toBe(3 + 1); | ||||||
|  |       } | ||||||
|  |       function fn2() { return fn1(); } | ||||||
|  |       function fn3() { return fn2(); } | ||||||
|  |       function fn4() { return fn3(); } | ||||||
|  |       fn4(); | ||||||
|  |       Error.stackTraceLimit = stackTraceLimit; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     it('should include only 3 frames for stack trace when Error.stackTraceLimit is 10', () => { | ||||||
|  |       const stackTraceLimit = Error.stackTraceLimit; | ||||||
|  |       Error.stackTraceLimit = 10; | ||||||
|  |       function fn1() { | ||||||
|  |         monitoredStore = createStore(counter, instrument(undefined, { trace: true, traceLimit: 3 })); | ||||||
|  |         monitoredLiftedStore = monitoredStore.liftedStore; | ||||||
|  |         monitoredStore.dispatch({ type: 'INCREMENT' }); | ||||||
|  | 
 | ||||||
|  |         exportedState = monitoredLiftedStore.getState(); | ||||||
|  |         expect(exportedState.actionsById[0].stack).toBe(undefined); | ||||||
|  |         expect(exportedState.actionsById[1].stack).toBeA('string'); | ||||||
|  |         expect(exportedState.actionsById[1].stack).toMatch(/at fn1 /); | ||||||
|  |         expect(exportedState.actionsById[1].stack).toMatch(/at fn2 /); | ||||||
|  |         expect(exportedState.actionsById[1].stack).toMatch(/at fn3 /); | ||||||
|  |         expect(exportedState.actionsById[1].stack).toNotMatch(/at fn4 /); | ||||||
|  |         expect(exportedState.actionsById[1].stack).toContain('instrument.spec.js'); | ||||||
|  |         expect(exportedState.actionsById[1].stack.split('\n').length).toBe(3 + 1); | ||||||
|  |       } | ||||||
|  |       function fn2() { return fn1(); } | ||||||
|  |       function fn3() { return fn2(); } | ||||||
|  |       function fn4() { return fn3(); } | ||||||
|  |       fn4(); | ||||||
|  |       Error.stackTraceLimit = stackTraceLimit; | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     it('should get stack trace from a function', () => { |     it('should get stack trace from a function', () => { | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user