mirror of
				https://github.com/reduxjs/redux-devtools.git
				synced 2025-10-31 07:57:39 +03:00 
			
		
		
		
	feat(trace): convert to TypeScript (#647)
* get started * progress * finish test * Revert that file * Move types to dev * Add enzyme types * Bump that version * prettier
This commit is contained in:
		
							parent
							
								
									d37e7d93e1
								
							
						
					
					
						commit
						03217001df
					
				|  | @ -84,6 +84,7 @@ | |||
|   "peerDependencies": { | ||||
|     "@types/react": "^16.3.18", | ||||
|     "react": "^16.3.0", | ||||
|     "redux": "^3.4.0 || ^4.0.0", | ||||
|     "redux-devtools-inspector-monitor": "^0.14.0" | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -1,13 +1,8 @@ | |||
| { | ||||
|   "presets": ["@babel/preset-env", "@babel/preset-react", "@babel/preset-flow"], | ||||
|   "plugins": [ | ||||
|     [ | ||||
|       "@babel/plugin-transform-runtime", | ||||
|       { | ||||
|         "regenerator": true | ||||
|       } | ||||
|     ], | ||||
|     ["@babel/plugin-proposal-decorators", { "legacy": true }], | ||||
|     "@babel/plugin-proposal-class-properties" | ||||
|   ] | ||||
|   "presets": [ | ||||
|     "@babel/preset-env", | ||||
|     "@babel/preset-react", | ||||
|     "@babel/preset-typescript" | ||||
|   ], | ||||
|   "plugins": ["@babel/plugin-proposal-class-properties"] | ||||
| } | ||||
|  |  | |||
							
								
								
									
										1
									
								
								packages/redux-devtools-trace-monitor/.eslintignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								packages/redux-devtools-trace-monitor/.eslintignore
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | |||
| lib | ||||
							
								
								
									
										21
									
								
								packages/redux-devtools-trace-monitor/.eslintrc.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								packages/redux-devtools-trace-monitor/.eslintrc.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | |||
| module.exports = { | ||||
|   extends: '../../.eslintrc', | ||||
|   overrides: [ | ||||
|     { | ||||
|       files: ['*.ts', '*.tsx'], | ||||
|       extends: '../../eslintrc.ts.react.base.json', | ||||
|       parserOptions: { | ||||
|         tsconfigRootDir: __dirname, | ||||
|         project: ['./tsconfig.json'], | ||||
|       }, | ||||
|     }, | ||||
|     { | ||||
|       files: ['test/*.ts', 'test/*.tsx'], | ||||
|       extends: '../../eslintrc.ts.react.jest.base.json', | ||||
|       parserOptions: { | ||||
|         tsconfigRootDir: __dirname, | ||||
|         project: ['./test/tsconfig.json'], | ||||
|       }, | ||||
|     }, | ||||
|   ], | ||||
| }; | ||||
							
								
								
									
										3
									
								
								packages/redux-devtools-trace-monitor/jest.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								packages/redux-devtools-trace-monitor/jest.config.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | |||
| module.exports = { | ||||
|   preset: 'ts-jest', | ||||
| }; | ||||
|  | @ -2,47 +2,61 @@ | |||
|   "name": "redux-devtools-trace-monitor", | ||||
|   "version": "0.1.3", | ||||
|   "description": "Submonitor for Redux DevTools inspector to show stack traces.", | ||||
|   "repository": "https://github.com/reduxjs/redux-devtools", | ||||
|   "homepage": "https://github.com/reduxjs/redux-devtools", | ||||
|   "homepage": "https://github.com/reduxjs/redux-devtools/tree/master/packages/redux-devtools-trace-monitor", | ||||
|   "license": "MIT", | ||||
|   "author": "Mark Erikson <mark@isquaredsoftware.com>", | ||||
|   "contributors": [ | ||||
|     "Mihail Diordiev <zalmoxisus@gmail.com> (https://github.com/zalmoxisus)" | ||||
|   ], | ||||
|   "license": "MIT", | ||||
|   "main": "lib/StackTraceTab.js", | ||||
|   "files": [ | ||||
|     "lib" | ||||
|   ], | ||||
|   "main": "lib/StackTraceTab.js", | ||||
|   "types": "lib/StackTraceTab.d.ts", | ||||
|   "repository": "https://github.com/reduxjs/redux-devtools", | ||||
|   "scripts": { | ||||
|     "build": "npm run build:types && npm run build:js", | ||||
|     "build:types": "tsc --emitDeclarationOnly", | ||||
|     "build:js": "babel src --out-dir lib --extensions \".ts,.tsx\" --source-maps inline", | ||||
|     "clean": "rimraf lib", | ||||
|     "build": "babel src --out-dir lib", | ||||
|     "test": "jest --no-cache", | ||||
|     "prepare": "npm run clean && npm run build", | ||||
|     "prepublishOnly": "npm run test && npm run clean && npm run build" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@babel/cli": "^7.10.5", | ||||
|     "@babel/core": "^7.11.1", | ||||
|     "@babel/plugin-proposal-class-properties": "^7.10.4", | ||||
|     "@babel/plugin-proposal-decorators": "^7.10.5", | ||||
|     "@babel/plugin-transform-runtime": "^7.11.0", | ||||
|     "@babel/preset-env": "^7.11.0", | ||||
|     "@babel/preset-flow": "^7.10.4", | ||||
|     "@babel/preset-react": "^7.10.4", | ||||
|     "enzyme": "^3.11.0", | ||||
|     "enzyme-adapter-react-16": "^1.15.3", | ||||
|     "enzyme-to-json": "^3.5.0", | ||||
|     "jest": "^26.2.2", | ||||
|     "react-dom": "^16.13.1", | ||||
|     "react-test-renderer": "^16.13.1", | ||||
|     "rimraf": "^3.0.2" | ||||
|     "test": "jest", | ||||
|     "lint": "eslint . --ext .ts,.tsx", | ||||
|     "lint:fix": "eslint . --ext .ts,.tsx --fix", | ||||
|     "type-check": "tsc --noEmit", | ||||
|     "type-check:watch": "npm run type-check -- --watch", | ||||
|     "preversion": "npm run type-check && npm run lint && npm run test", | ||||
|     "prepublishOnly": "npm run clean && npm run build" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@babel/code-frame": "^7.10.4", | ||||
|     "@types/chrome": "^0.0.124", | ||||
|     "anser": "^1.4.9", | ||||
|     "html-entities": "^1.3.1", | ||||
|     "react": "^16.13.1", | ||||
|     "redux-devtools-themes": "^1.0.0", | ||||
|     "settle-promise": "^1.0.0" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@types/babel__code-frame": "^7.0.2", | ||||
|     "@types/enzyme": "^3.10.5", | ||||
|     "@types/enzyme-adapter-react-16": "^1.0.6", | ||||
|     "@types/html-entities": "^1.2.16", | ||||
|     "@types/react": "^16.9.46", | ||||
|     "@types/redux-devtools-themes": "^1.0.0", | ||||
|     "enzyme": "^3.11.0", | ||||
|     "enzyme-adapter-react-16": "^1.15.3", | ||||
|     "enzyme-to-json": "^3.5.0", | ||||
|     "react": "^16.13.1", | ||||
|     "react-dom": "^16.13.1", | ||||
|     "react-test-renderer": "^16.13.1", | ||||
|     "redux": "^4.0.5", | ||||
|     "redux-devtools": "^3.7.0", | ||||
|     "redux-devtools-inspector-monitor": "^0.14.0" | ||||
|   }, | ||||
|   "peerDependencies": { | ||||
|     "@types/react": "^16.3.18", | ||||
|     "react": "^16.3.0", | ||||
|     "redux": "^3.4.0 || ^4.0.0", | ||||
|     "redux-devtools": "^3.4.0", | ||||
|     "redux-devtools-inspector-monitor": "^0.14.0" | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -3,26 +3,45 @@ import React, { Component } from 'react'; | |||
| import { getStackFrames } from './react-error-overlay/utils/getStackFrames'; | ||||
| import StackTrace from './react-error-overlay/containers/StackTrace'; | ||||
| import openFile from './openFile'; | ||||
| import { Action } from 'redux'; | ||||
| import { TabComponentProps } from 'redux-devtools-inspector-monitor'; | ||||
| import StackFrame from './react-error-overlay/utils/stack-frame'; | ||||
| import { ErrorLocation } from './react-error-overlay/utils/parseCompileError'; | ||||
| 
 | ||||
| const rootStyle = { padding: '5px 10px' }; | ||||
| 
 | ||||
| export default class StackTraceTab extends Component { | ||||
| interface Props<S, A extends Action<unknown>> extends TabComponentProps<S, A> { | ||||
|   openFile: ( | ||||
|     fileName: string, | ||||
|     lineNumber: number, | ||||
|     stackFrame: StackFrame | ||||
|   ) => void; | ||||
| } | ||||
| 
 | ||||
| interface State { | ||||
|   stackFrames: StackFrame[]; | ||||
|   currentError?: Error; | ||||
|   showDocsLink?: boolean; | ||||
| } | ||||
| 
 | ||||
| export default class StackTraceTab< | ||||
|   S, | ||||
|   A extends Action<unknown> | ||||
| > extends Component<Props<S, A>, State> { | ||||
|   static defaultProps = { | ||||
|     openFile, | ||||
|   }; | ||||
|   constructor(props) { | ||||
|     super(props); | ||||
| 
 | ||||
|     this.state = { | ||||
|       stackFrames: [], | ||||
|     }; | ||||
|   } | ||||
|   state: State = { | ||||
|     stackFrames: [], | ||||
|   }; | ||||
| 
 | ||||
|   componentDidMount() { | ||||
|     // console.log("StackTraceTab mounted");
 | ||||
|     this.checkForStackTrace(); | ||||
|   } | ||||
| 
 | ||||
|   componentDidUpdate(prevProps) { | ||||
|   componentDidUpdate(prevProps: Props<S, A>) { | ||||
|     const { action, actions } = prevProps; | ||||
| 
 | ||||
|     if (action !== this.props.action || actions !== this.props.actions) { | ||||
|  | @ -47,25 +66,32 @@ export default class StackTraceTab extends Component { | |||
|         stack: liftedAction.stack, | ||||
|       }); | ||||
| 
 | ||||
|       getStackFrames(deserializedError).then((stackFrames) => { | ||||
|         /* eslint-disable no-console */ | ||||
|         if (process.env.NODE_ENV === 'development') | ||||
|           console.log('Stack frames: ', stackFrames); | ||||
|         /* eslint-enable no-console */ | ||||
|         this.setState({ stackFrames, currentError: deserializedError }); | ||||
|       }); | ||||
|       getStackFrames(deserializedError) | ||||
|         .then((stackFrames) => { | ||||
|           /* eslint-disable no-console */ | ||||
|           if (process.env.NODE_ENV === 'development') | ||||
|             console.log('Stack frames: ', stackFrames); | ||||
|           /* eslint-enable no-console */ | ||||
|           this.setState({ | ||||
|             stackFrames: stackFrames!, | ||||
|             currentError: deserializedError, | ||||
|           }); | ||||
|         }) | ||||
|         .catch(() => { | ||||
|           // noop
 | ||||
|         }); | ||||
|     } else { | ||||
|       this.setState({ | ||||
|         stackFrames: [], | ||||
|         showDocsLink: | ||||
|           liftedAction.action && | ||||
|           liftedAction.action.type && | ||||
|           liftedAction.action.type !== '@@INIT', | ||||
|           liftedAction!.action && | ||||
|           liftedAction!.action.type && | ||||
|           liftedAction!.action.type !== '@@INIT', | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   onStackLocationClicked = (fileLocation = {}) => { | ||||
|   onStackLocationClicked = (fileLocation: Partial<ErrorLocation> = {}) => { | ||||
|     // console.log("Stack location args: ", ...args);
 | ||||
| 
 | ||||
|     const { fileName, lineNumber } = fileLocation; | ||||
|  | @ -93,7 +119,7 @@ export default class StackTraceTab extends Component { | |||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   openDocs = (e) => { | ||||
|   openDocs: React.MouseEventHandler<HTMLAnchorElement> = (e) => { | ||||
|     e.stopPropagation(); | ||||
|     window.open( | ||||
|       'https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/Features/Trace.md' | ||||
|  | @ -1,31 +1,35 @@ | |||
| import StackFrame from './react-error-overlay/utils/stack-frame'; | ||||
| 
 | ||||
| const isFF = navigator.userAgent.indexOf('Firefox') !== -1; | ||||
| 
 | ||||
| function openResource(fileName, lineNumber, stackFrame) { | ||||
| function openResource( | ||||
|   fileName: string, | ||||
|   lineNumber: number, | ||||
|   stackFrame: StackFrame | ||||
| ) { | ||||
|   const adjustedLineNumber = Math.max(lineNumber - 1, 0); | ||||
|   chrome.devtools.panels.openResource( | ||||
|     fileName, | ||||
|     adjustedLineNumber, | ||||
|     (result) => { | ||||
|       //console.log("openResource callback args: ", callbackArgs);
 | ||||
|       if (result.isError) { | ||||
|         const { | ||||
|           fileName: finalFileName, | ||||
|           lineNumber: finalLineNumber, | ||||
|         } = stackFrame; | ||||
|         const adjustedLineNumber = Math.max(finalLineNumber - 1, 0); | ||||
|         chrome.devtools.panels.openResource( | ||||
|           finalFileName, | ||||
|           adjustedLineNumber, | ||||
|           (/* result */) => { | ||||
|             // console.log("openResource result: ", result);
 | ||||
|           } | ||||
|         ); | ||||
|       } | ||||
|   chrome.devtools.panels.openResource(fileName, adjustedLineNumber, ((result: { | ||||
|     isError?: boolean; | ||||
|   }) => { | ||||
|     //console.log("openResource callback args: ", callbackArgs);
 | ||||
|     if (result.isError) { | ||||
|       const { | ||||
|         fileName: finalFileName, | ||||
|         lineNumber: finalLineNumber, | ||||
|       } = stackFrame; | ||||
|       const adjustedLineNumber = Math.max(finalLineNumber! - 1, 0); | ||||
|       chrome.devtools.panels.openResource( | ||||
|         finalFileName!, | ||||
|         adjustedLineNumber, | ||||
|         (/* result */) => { | ||||
|           // console.log("openResource result: ", result);
 | ||||
|         } | ||||
|       ); | ||||
|     } | ||||
|   ); | ||||
|   }) as () => void); | ||||
| } | ||||
| 
 | ||||
| function openAndCloseTab(url) { | ||||
| function openAndCloseTab(url: string) { | ||||
|   chrome.tabs.create({ url }, (tab) => { | ||||
|     const removeTab = () => { | ||||
|       chrome.windows.onFocusChanged.removeListener(removeTab); | ||||
|  | @ -45,19 +49,19 @@ function openAndCloseTab(url) { | |||
|   }); | ||||
| } | ||||
| 
 | ||||
| function openInIframe(url) { | ||||
| function openInIframe(url: string) { | ||||
|   const iframe = document.createElement('iframe'); | ||||
|   iframe.src = url; | ||||
|   iframe.style = 'display:none'; | ||||
|   iframe.style.display = 'none'; | ||||
|   document.body.appendChild(iframe); | ||||
|   setTimeout(() => iframe.parentNode.removeChild(iframe), 3000); | ||||
|   setTimeout(() => iframe.parentNode!.removeChild(iframe), 3000); | ||||
| } | ||||
| 
 | ||||
| function openInEditor(editor, path, stackFrame) { | ||||
| function openInEditor(editor: string, path: string, stackFrame: StackFrame) { | ||||
|   const projectPath = path.replace(/\/$/, ''); | ||||
|   const file = | ||||
|     stackFrame._originalFileName || | ||||
|     stackFrame.finalFileName || | ||||
|     ((stackFrame as unknown) as { finalFileName: string }).finalFileName || | ||||
|     stackFrame.fileName || | ||||
|     ''; | ||||
|   let filePath = /^https?:\/\//.test(file) | ||||
|  | @ -95,7 +99,11 @@ function openInEditor(editor, path, stackFrame) { | |||
|   } | ||||
| } | ||||
| 
 | ||||
| export default function openFile(fileName, lineNumber, stackFrame) { | ||||
| export default function openFile( | ||||
|   fileName: string, | ||||
|   lineNumber: number, | ||||
|   stackFrame: StackFrame | ||||
| ) { | ||||
|   if (process.env.NODE_ENV === 'development') | ||||
|     // eslint-disable-next-line no-console
 | ||||
|     console.log(fileName, lineNumber, stackFrame); | ||||
|  | @ -5,10 +5,9 @@ | |||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| /* @flow */ | ||||
| import React from 'react'; | ||||
| import React, { CSSProperties } from 'react'; | ||||
| 
 | ||||
| const preStyle = { | ||||
| const preStyle: CSSProperties = { | ||||
|   position: 'relative', | ||||
|   display: 'block', | ||||
|   backgroundColor: '#000', | ||||
|  | @ -24,10 +23,10 @@ const codeStyle = { | |||
|   fontFamily: 'Consolas, Menlo, monospace', | ||||
| }; | ||||
| 
 | ||||
| type CodeBlockPropsType = {| | ||||
|   main: boolean, | ||||
|   codeHTML: string, | ||||
| |}; | ||||
| interface CodeBlockPropsType { | ||||
|   main: boolean; | ||||
|   codeHTML: string; | ||||
| } | ||||
| 
 | ||||
| function CodeBlock(props: CodeBlockPropsType) { | ||||
|   const codeBlock = { __html: props.codeHTML }; | ||||
|  | @ -5,13 +5,10 @@ | |||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| /* @flow */ | ||||
| import React, { Component } from 'react'; | ||||
| import React, { Component, CSSProperties, ReactNode } from 'react'; | ||||
| import { nicinabox as theme } from 'redux-devtools-themes'; | ||||
| 
 | ||||
| import type { Element as ReactElement } from 'react'; | ||||
| 
 | ||||
| const _collapsibleStyle = { | ||||
| const _collapsibleStyle: CSSProperties = { | ||||
|   color: theme.base06, | ||||
|   backgroundColor: theme.base01, | ||||
|   cursor: 'pointer', | ||||
|  | @ -24,26 +21,27 @@ const _collapsibleStyle = { | |||
|   lineHeight: '1.5', | ||||
| }; | ||||
| 
 | ||||
| const collapsibleCollapsedStyle = { | ||||
| const collapsibleCollapsedStyle: CSSProperties = { | ||||
|   ..._collapsibleStyle, | ||||
|   marginBottom: '1.5em', | ||||
| }; | ||||
| 
 | ||||
| const collapsibleExpandedStyle = { | ||||
| const collapsibleExpandedStyle: CSSProperties = { | ||||
|   ..._collapsibleStyle, | ||||
|   marginBottom: '0.6em', | ||||
| }; | ||||
| 
 | ||||
| type Props = {| | ||||
|   children: ReactElement<any>[], | ||||
| |}; | ||||
| interface Props { | ||||
|   collapsedByDefault?: boolean; | ||||
|   children: ReactNode[]; | ||||
| } | ||||
| 
 | ||||
| type State = {| | ||||
|   collapsed: boolean, | ||||
| |}; | ||||
| interface State { | ||||
|   collapsed: boolean | undefined; | ||||
| } | ||||
| 
 | ||||
| class Collapsible extends Component<Props, State> { | ||||
|   state = { | ||||
|   state: State = { | ||||
|     collapsed: undefined, | ||||
|   }; | ||||
| 
 | ||||
|  | @ -53,7 +51,7 @@ class Collapsible extends Component<Props, State> { | |||
|     })); | ||||
|   }; | ||||
| 
 | ||||
|   isCollapsed = (state) => | ||||
|   isCollapsed = (state: State) => | ||||
|     state.collapsed === undefined | ||||
|       ? this.props.collapsedByDefault | ||||
|       : state.collapsed; | ||||
|  | @ -5,8 +5,7 @@ | |||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| /* @flow */ | ||||
| import React, { Component } from 'react'; | ||||
| import React, { Component, CSSProperties } from 'react'; | ||||
| import CodeBlock from './StackFrameCodeBlock'; | ||||
| import { getPrettyURL } from '../utils/getPrettyURL'; | ||||
| import { nicinabox as theme } from 'redux-devtools-themes'; | ||||
|  | @ -14,22 +13,22 @@ import { nicinabox as theme } from 'redux-devtools-themes'; | |||
| import type { StackFrame as StackFrameType } from '../utils/stack-frame'; | ||||
| import type { ErrorLocation } from '../utils/parseCompileError'; | ||||
| 
 | ||||
| const linkStyle = { | ||||
| const linkStyle: CSSProperties = { | ||||
|   fontSize: '0.9em', | ||||
|   marginBottom: '0.9em', | ||||
| }; | ||||
| 
 | ||||
| const anchorStyle = { | ||||
| const anchorStyle: CSSProperties = { | ||||
|   textDecoration: 'none', | ||||
|   color: theme.base05, | ||||
|   cursor: 'pointer', | ||||
| }; | ||||
| 
 | ||||
| const codeAnchorStyle = { | ||||
| const codeAnchorStyle: CSSProperties = { | ||||
|   cursor: 'pointer', | ||||
| }; | ||||
| 
 | ||||
| const toggleStyle = { | ||||
| const toggleStyle: CSSProperties = { | ||||
|   marginBottom: '1.5em', | ||||
|   color: theme.base05, | ||||
|   cursor: 'pointer', | ||||
|  | @ -44,20 +43,20 @@ const toggleStyle = { | |||
|   lineHeight: '1.5', | ||||
| }; | ||||
| 
 | ||||
| type Props = {| | ||||
|   frame: StackFrameType, | ||||
|   contextSize: number, | ||||
|   critical: boolean, | ||||
|   showCode: boolean, | ||||
|   editorHandler: (errorLoc: ErrorLocation) => void, | ||||
| |}; | ||||
| interface Props { | ||||
|   frame: StackFrameType; | ||||
|   contextSize: number; | ||||
|   critical: boolean; | ||||
|   showCode: boolean; | ||||
|   editorHandler: (errorLoc: ErrorLocation) => void; | ||||
| } | ||||
| 
 | ||||
| type State = {| | ||||
|   compiled: boolean, | ||||
| |}; | ||||
| interface State { | ||||
|   compiled: boolean; | ||||
| } | ||||
| 
 | ||||
| class StackFrame extends Component<Props, State> { | ||||
|   state = { | ||||
|   state: State = { | ||||
|     compiled: false, | ||||
|   }; | ||||
| 
 | ||||
|  | @ -93,7 +92,9 @@ class StackFrame extends Component<Props, State> { | |||
|     this.props.editorHandler(errorLoc); | ||||
|   }; | ||||
| 
 | ||||
|   onKeyDown = (e /* : SyntheticKeyboardEvent<> */) => { | ||||
|   onKeyDown: React.KeyboardEventHandler<HTMLSpanElement> = ( | ||||
|     e /* : SyntheticKeyboardEvent<> */ | ||||
|   ) => { | ||||
|     if (e.key === 'Enter') { | ||||
|       this.editorHandler(); | ||||
|     } | ||||
|  | @ -162,10 +163,10 @@ class StackFrame extends Component<Props, State> { | |||
|         <div>{functionName}</div> | ||||
|         <div style={linkStyle}> | ||||
|           <span | ||||
|             style={canOpenInEditor ? anchorStyle : null} | ||||
|             onClick={canOpenInEditor ? this.editorHandler : null} | ||||
|             onKeyDown={canOpenInEditor ? this.onKeyDown : null} | ||||
|             tabIndex={canOpenInEditor ? '0' : null} | ||||
|             style={canOpenInEditor ? anchorStyle : undefined} | ||||
|             onClick={canOpenInEditor ? this.editorHandler : undefined} | ||||
|             onKeyDown={canOpenInEditor ? this.onKeyDown : undefined} | ||||
|             tabIndex={canOpenInEditor ? 0 : undefined} | ||||
|           > | ||||
|             {url} | ||||
|           </span> | ||||
|  | @ -173,8 +174,8 @@ class StackFrame extends Component<Props, State> { | |||
|         {codeBlockProps && ( | ||||
|           <span> | ||||
|             <span | ||||
|               onClick={canOpenInEditor ? this.editorHandler : null} | ||||
|               style={canOpenInEditor ? codeAnchorStyle : null} | ||||
|               onClick={canOpenInEditor ? this.editorHandler : undefined} | ||||
|               style={canOpenInEditor ? codeAnchorStyle : undefined} | ||||
|             > | ||||
|               <CodeBlock {...codeBlockProps} /> | ||||
|             </span> | ||||
|  | @ -5,40 +5,31 @@ | |||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| /* @flow */ | ||||
| import React from 'react'; | ||||
| import CodeBlock from '../components/CodeBlock'; | ||||
| import { applyStyles } from '../utils/dom/css'; | ||||
| import { absolutifyCaret } from '../utils/dom/absolutifyCaret'; | ||||
| // import type { ScriptLine } from '../utils/stack-frame';
 | ||||
| import { ScriptLine } from '../utils/stack-frame'; | ||||
| import generateAnsiHTML from '../utils/generateAnsiHTML'; | ||||
| 
 | ||||
| import { codeFrameColumns } from '@babel/code-frame'; | ||||
| import { nicinabox as theme } from 'redux-devtools-themes'; | ||||
| 
 | ||||
| /* | ||||
| type StackFrameCodeBlockPropsType = {| | ||||
|   lines: ScriptLine[], | ||||
|   lineNum: number, | ||||
|   columnNum: ?number, | ||||
|   contextSize: number, | ||||
|   main: boolean, | ||||
| |}; | ||||
| interface StackFrameCodeBlockPropsType { | ||||
|   lines: ScriptLine[]; | ||||
|   lineNum: number; | ||||
|   columnNum: number | null | undefined; | ||||
|   contextSize: number; | ||||
|   main: boolean; | ||||
| } | ||||
| 
 | ||||
| // Exact type workaround for spread operator.
 | ||||
| // See: https://github.com/facebook/flow/issues/2405
 | ||||
| type Exact<T> = $Shape<T>; | ||||
| */ | ||||
| 
 | ||||
| function StackFrameCodeBlock( | ||||
|   props /* : Exact<StackFrameCodeBlockPropsType> */ | ||||
| ) { | ||||
| function StackFrameCodeBlock(props: StackFrameCodeBlockPropsType) { | ||||
|   const { lines, lineNum, columnNum, contextSize, main } = props; | ||||
|   const sourceCode = []; | ||||
|   const sourceCode: string[] = []; | ||||
|   let whiteSpace = Infinity; | ||||
|   lines.forEach(function (e) { | ||||
|     const { content: text } = e; | ||||
|     const m = text.match(/^\s*/); | ||||
|     const m = /^\s*/.exec(text); | ||||
|     if (text === '') { | ||||
|       return; | ||||
|     } | ||||
|  | @ -86,16 +77,16 @@ function StackFrameCodeBlock( | |||
|     const ccn2 = node.childNodes; | ||||
|     for (let index2 = 0; index2 < ccn2.length; ++index2) { | ||||
|       const lineNode = ccn2[index2]; | ||||
|       const text = lineNode.innerText; | ||||
|       const text = (lineNode as HTMLElement).innerText; | ||||
|       if (text == null) { | ||||
|         continue; | ||||
|       } | ||||
|       if (text.indexOf(' ' + lineNum + ' |') === -1) { | ||||
|       if (text.indexOf(` ${lineNum} |`) === -1) { | ||||
|         continue; | ||||
|       } | ||||
|       // $FlowFixMe
 | ||||
|       applyStyles(node, { | ||||
|         'background-color': main ? theme.base02 : theme.base01, | ||||
|       applyStyles(node as HTMLElement, { | ||||
|         backgroundColor: main ? theme.base02 : theme.base01, | ||||
|       }); | ||||
|       // eslint-disable-next-line
 | ||||
|       break oLoop; | ||||
|  | @ -5,8 +5,7 @@ | |||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| /* @flow */ | ||||
| import React, { Component } from 'react'; | ||||
| import React, { Component, ReactElement } from 'react'; | ||||
| import StackFrame from './StackFrame'; | ||||
| import Collapsible from '../components/Collapsible'; | ||||
| import { isInternalFile } from '../utils/isInternalFile'; | ||||
|  | @ -22,19 +21,19 @@ const traceStyle = { | |||
|   overflow: 'auto', | ||||
| }; | ||||
| 
 | ||||
| type Props = {| | ||||
|   stackFrames: StackFrameType[], | ||||
|   errorName: string, | ||||
|   contextSize: number, | ||||
|   editorHandler: (errorLoc: ErrorLocation) => void, | ||||
| |}; | ||||
| interface Props { | ||||
|   stackFrames: StackFrameType[]; | ||||
|   errorName: string; | ||||
|   contextSize: number; | ||||
|   editorHandler: (errorLoc: ErrorLocation) => void; | ||||
| } | ||||
| 
 | ||||
| class StackTrace extends Component<Props> { | ||||
|   renderFrames() { | ||||
|     const { stackFrames, errorName, contextSize, editorHandler } = this.props; | ||||
|     const renderedFrames = []; | ||||
|     const renderedFrames: ReactElement[] = []; | ||||
|     let hasReachedAppCode = false, | ||||
|       currentBundle = [], | ||||
|       currentBundle: ReactElement[] = [], | ||||
|       bundleCount = 0, | ||||
|       anyNodeExpanded = false; | ||||
| 
 | ||||
|  | @ -55,7 +54,7 @@ class StackTrace extends Component<Props> { | |||
| 
 | ||||
|       const frameEle = ( | ||||
|         <StackFrame | ||||
|           key={'frame-' + index} | ||||
|           key={`frame-${index}`} | ||||
|           frame={frame} | ||||
|           contextSize={contextSize} | ||||
|           critical={index === 0} | ||||
|  | @ -77,7 +76,7 @@ class StackTrace extends Component<Props> { | |||
|           renderedFrames.push( | ||||
|             <Collapsible | ||||
|               collapsedByDefault={anyNodeExpanded} | ||||
|               key={'bundle-' + bundleCount} | ||||
|               key={`bundle-${bundleCount}`} | ||||
|             > | ||||
|               {currentBundle} | ||||
|             </Collapsible> | ||||
|  | @ -5,8 +5,7 @@ | |||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| /* @flow */ | ||||
| function removeNextBr(parent, component: ?Element) { | ||||
| function removeNextBr(parent: Node, component: Element | null | undefined) { | ||||
|   while (component != null && component.tagName.toLowerCase() !== 'br') { | ||||
|     component = component.nextElementSibling; | ||||
|   } | ||||
|  | @ -18,7 +17,7 @@ function removeNextBr(parent, component: ?Element) { | |||
| function absolutifyCaret(component: Node) { | ||||
|   const ccn = component.childNodes; | ||||
|   for (let index = 0; index < ccn.length; ++index) { | ||||
|     const c = ccn[index]; | ||||
|     const c = ccn[index] as HTMLElement; | ||||
|     // $FlowFixMe
 | ||||
|     if (c.tagName.toLowerCase() !== 'span') { | ||||
|       continue; | ||||
|  | @ -5,9 +5,8 @@ | |||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| /* @flow */ | ||||
| let injectedCount = 0; | ||||
| const injectedCache = {}; | ||||
| const injectedCache: { [key: number]: HTMLStyleElement } = {}; | ||||
| 
 | ||||
| function getHead(document: Document) { | ||||
|   return document.head || document.getElementsByTagName('head')[0]; | ||||
|  | @ -33,14 +32,17 @@ function removeCss(document: Document, ref: number) { | |||
|   delete injectedCache[ref]; | ||||
| } | ||||
| 
 | ||||
| function applyStyles(element: HTMLElement, styles: Object) { | ||||
| function applyStyles( | ||||
|   element: HTMLElement, | ||||
|   styles: Partial<CSSStyleDeclaration> | ||||
| ) { | ||||
|   element.setAttribute('style', ''); | ||||
|   for (const key in styles) { | ||||
|     if (!Object.prototype.hasOwnProperty.call(styles, key)) { | ||||
|       continue; | ||||
|     } | ||||
|     // $FlowFixMe
 | ||||
|     element.style[key] = styles[key]; | ||||
|     element.style[key] = styles[key]!; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|  | @ -5,15 +5,13 @@ | |||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| /* @flow */ | ||||
| 
 | ||||
| import Anser from 'anser'; | ||||
| import { nicinabox as theme } from 'redux-devtools-themes'; | ||||
| import { AllHtmlEntities as Entities } from 'html-entities'; | ||||
| 
 | ||||
| var entities = new Entities(); | ||||
| const entities = new Entities(); | ||||
| 
 | ||||
| var anserMap = { | ||||
| const anserMap = { | ||||
|   'ansi-bright-black': theme.base03, | ||||
|   'ansi-bright-yellow': theme.base0A, | ||||
|   'ansi-yellow': theme.base0B, | ||||
|  | @ -29,25 +27,25 @@ var anserMap = { | |||
| }; | ||||
| 
 | ||||
| function generateAnsiHTML(txt: string): string { | ||||
|   var arr = new Anser().ansiToJson(entities.encode(txt), { | ||||
|   const arr = new Anser().ansiToJson(entities.encode(txt), { | ||||
|     use_classes: true, | ||||
|   }); | ||||
| 
 | ||||
|   var result = ''; | ||||
|   var open = false; | ||||
|   for (var index = 0; index < arr.length; ++index) { | ||||
|     var c = arr[index]; | ||||
|     var content = c.content, | ||||
|   let result = ''; | ||||
|   let open = false; | ||||
|   for (let index = 0; index < arr.length; ++index) { | ||||
|     const c = arr[index]; | ||||
|     const content = c.content, | ||||
|       fg = c.fg; | ||||
| 
 | ||||
|     var contentParts = content.split('\n'); | ||||
|     for (var _index = 0; _index < contentParts.length; ++_index) { | ||||
|     const contentParts = content.split('\n'); | ||||
|     for (let _index = 0; _index < contentParts.length; ++_index) { | ||||
|       if (!open) { | ||||
|         result += '<span data-ansi-line="true">'; | ||||
|         open = true; | ||||
|       } | ||||
|       var part = contentParts[_index].replace('\r', ''); | ||||
|       var color = anserMap[fg]; | ||||
|       const part = contentParts[_index].replace('\r', ''); | ||||
|       const color = anserMap[fg as keyof typeof anserMap]; | ||||
|       if (color != null) { | ||||
|         result += '<span style="color: ' + color + ';">' + part + '</span>'; | ||||
|       } else { | ||||
|  | @ -5,7 +5,6 @@ | |||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| /* @flow */ | ||||
| import { ScriptLine } from './stack-frame'; | ||||
| 
 | ||||
| /** | ||||
|  | @ -5,14 +5,13 @@ | |||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| /* @flow */ | ||||
| function getPrettyURL( | ||||
|   sourceFileName: ?string, | ||||
|   sourceLineNumber: ?number, | ||||
|   sourceColumnNumber: ?number, | ||||
|   fileName: ?string, | ||||
|   lineNumber: ?number, | ||||
|   columnNumber: ?number, | ||||
|   sourceFileName: string | null | undefined, | ||||
|   sourceLineNumber: number | null | undefined, | ||||
|   sourceColumnNumber: number | null | undefined, | ||||
|   fileName: string | null | undefined, | ||||
|   lineNumber: number | null | undefined, | ||||
|   columnNumber: number | null | undefined, | ||||
|   compiled: boolean | ||||
| ): string { | ||||
|   let prettyURL; | ||||
|  | @ -26,16 +25,16 @@ function getPrettyURL( | |||
|     } else { | ||||
|       prettyURL = sourceFileName; | ||||
|     } | ||||
|     prettyURL += ':' + sourceLineNumber; | ||||
|     prettyURL += `:${sourceLineNumber}`; | ||||
|     // Note: we intentionally skip 0's because they're produced by cheap Webpack maps
 | ||||
|     if (sourceColumnNumber) { | ||||
|       prettyURL += ':' + sourceColumnNumber; | ||||
|       prettyURL += `:${sourceColumnNumber}`; | ||||
|     } | ||||
|   } else if (fileName && typeof lineNumber === 'number') { | ||||
|     prettyURL = fileName + ':' + lineNumber; | ||||
|     prettyURL = `${fileName}:${lineNumber}`; | ||||
|     // Note: we intentionally skip 0's because they're produced by cheap Webpack maps
 | ||||
|     if (columnNumber) { | ||||
|       prettyURL += ':' + columnNumber; | ||||
|       prettyURL += `:${columnNumber}`; | ||||
|     } | ||||
|   } else { | ||||
|     prettyURL = 'unknown'; | ||||
|  | @ -5,8 +5,7 @@ | |||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| /* @flow */ | ||||
| import { SourceMapConsumer } from 'source-map'; | ||||
| import { RawSourceMap, SourceMapConsumer } from 'source-map'; | ||||
| 
 | ||||
| /** | ||||
|  * A wrapped instance of a <code>{@link https://github.com/mozilla/source-map SourceMapConsumer}</code>.
 | ||||
|  | @ -16,7 +15,7 @@ import { SourceMapConsumer } from 'source-map'; | |||
| class SourceMap { | ||||
|   __source_map: SourceMapConsumer; | ||||
| 
 | ||||
|   constructor(sourceMap) { | ||||
|   constructor(sourceMap: SourceMapConsumer) { | ||||
|     this.__source_map = sourceMap; | ||||
|   } | ||||
| 
 | ||||
|  | @ -28,7 +27,7 @@ class SourceMap { | |||
|   getOriginalPosition( | ||||
|     line: number, | ||||
|     column: number | ||||
|   ): { source: string, line: number, column: number } { | ||||
|   ): { source: string; line: number; column: number } { | ||||
|     const { | ||||
|       line: l, | ||||
|       column: c, | ||||
|  | @ -50,7 +49,7 @@ class SourceMap { | |||
|     source: string, | ||||
|     line: number, | ||||
|     column: number | ||||
|   ): { line: number, column: number } { | ||||
|   ): { line: number; column: number } { | ||||
|     const { line: l, column: c } = this.__source_map.generatedPositionFor({ | ||||
|       source, | ||||
|       line, | ||||
|  | @ -71,7 +70,7 @@ class SourceMap { | |||
|   } | ||||
| 
 | ||||
|   getSources(): string[] { | ||||
|     return this.__source_map.sources; | ||||
|     return ((this.__source_map as unknown) as { sources: string[] }).sources; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|  | @ -82,7 +81,7 @@ function extractSourceMapUrl( | |||
|   const regex = /\/\/[#@] ?sourceMappingURL=([^\s'"]+)\s*$/gm; | ||||
|   let match = null; | ||||
|   for (;;) { | ||||
|     let next = regex.exec(fileContents); | ||||
|     const next = regex.exec(fileContents); | ||||
|     if (next == null) { | ||||
|       break; | ||||
|     } | ||||
|  | @ -107,7 +106,7 @@ async function getSourceMap( | |||
|   let sm = await extractSourceMapUrl(fileUri, fileContents); | ||||
|   if (sm.indexOf('data:') === 0) { | ||||
|     const base64 = /^data:application\/json;([\w=:"-]+;)*base64,/; | ||||
|     const match2 = sm.match(base64); | ||||
|     const match2 = base64.exec(sm); | ||||
|     if (!match2) { | ||||
|       throw new Error( | ||||
|         'Sorry, non-base64 inline source-map encoding is not supported.' | ||||
|  | @ -116,7 +115,9 @@ async function getSourceMap( | |||
|     sm = sm.substring(match2[0].length); | ||||
|     sm = window.atob(sm); | ||||
|     sm = JSON.parse(sm); | ||||
|     return new SourceMap(new SourceMapConsumer(sm)); | ||||
|     return new SourceMap( | ||||
|       new SourceMapConsumer((sm as unknown) as RawSourceMap) | ||||
|     ); | ||||
|   } else { | ||||
|     const index = fileUri.lastIndexOf('/'); | ||||
|     const url = fileUri.substring(0, index + 1) + sm; | ||||
|  | @ -5,7 +5,6 @@ | |||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| /* @flow */ | ||||
| import type { StackFrame } from './stack-frame'; | ||||
| import { parse } from './parser'; | ||||
| import { map } from './mapper'; | ||||
|  | @ -14,15 +13,21 @@ import { toExclude } from '../../presets'; | |||
| 
 | ||||
| function getStackFrames( | ||||
|   error: Error, | ||||
|   unhandledRejection: boolean = false, // eslint-disable-line no-unused-vars
 | ||||
|   contextSize: number = 3 | ||||
|   unhandledRejection = false, // eslint-disable-line no-unused-vars
 | ||||
|   contextSize = 3 | ||||
| ): Promise<StackFrame[] | null> { | ||||
|   const parsedFrames = parse(error); | ||||
|   let enhancedFramesPromise; | ||||
|   if (error.__unmap_source) { | ||||
|   if ( | ||||
|     ((error as unknown) as { | ||||
|       __unmap_source: string | { uri: string; contents: string }; | ||||
|     }).__unmap_source | ||||
|   ) { | ||||
|     enhancedFramesPromise = unmap( | ||||
|       // $FlowFixMe
 | ||||
|       error.__unmap_source, | ||||
|       ((error as unknown) as { | ||||
|         __unmap_source: string | { uri: string; contents: string }; | ||||
|       }).__unmap_source, | ||||
|       parsedFrames, | ||||
|       contextSize | ||||
|     ); | ||||
|  | @ -44,7 +49,7 @@ function getStackFrames( | |||
|         (functionName == null || | ||||
|           functionName.indexOf('__stack_frame_overlay_proxy_console__') === | ||||
|             -1) && | ||||
|         !toExclude.test(fileName) | ||||
|         !toExclude.test(fileName!) | ||||
|     ); | ||||
|   }); | ||||
| } | ||||
|  | @ -5,8 +5,7 @@ | |||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| /* @flow */ | ||||
| function isBultinErrorName(errorName: ?string) { | ||||
| function isBultinErrorName(errorName: string | null | undefined) { | ||||
|   switch (errorName) { | ||||
|     case 'EvalError': | ||||
|     case 'InternalError': | ||||
|  | @ -5,8 +5,10 @@ | |||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| /* @flow */ | ||||
| function isInternalFile(sourceFileName: ?string, fileName: ?string) { | ||||
| function isInternalFile( | ||||
|   sourceFileName: string | null | undefined, | ||||
|   fileName: string | null | undefined | ||||
| ) { | ||||
|   return ( | ||||
|     sourceFileName == null || | ||||
|     sourceFileName === '' || | ||||
|  | @ -5,7 +5,6 @@ | |||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| /* @flow */ | ||||
| import StackFrame from './stack-frame'; | ||||
| import { getSourceMap } from './getSourceMap'; | ||||
| import { getLinesAround } from './getLinesAround'; | ||||
|  | @ -18,7 +17,7 @@ import { settle } from 'settle-promise'; | |||
|  */ | ||||
| async function map( | ||||
|   frames: StackFrame[], | ||||
|   contextLines: number = 3 | ||||
|   contextLines = 3 | ||||
| ): Promise<StackFrame[]> { | ||||
|   const cache: any = {}; | ||||
|   const files: string[] = []; | ||||
|  | @ -41,7 +40,7 @@ async function map( | |||
|   ); | ||||
|   return frames.map((frame) => { | ||||
|     const { functionName, fileName, lineNumber, columnNumber } = frame; | ||||
|     let { map, fileSource } = cache[fileName] || {}; | ||||
|     const { map, fileSource } = cache[fileName!] || {}; | ||||
|     if (map == null || lineNumber == null) { | ||||
|       return frame; | ||||
|     } | ||||
|  | @ -1,11 +1,10 @@ | |||
| // @flow
 | ||||
| import Anser from 'anser'; | ||||
| 
 | ||||
| export type ErrorLocation = {| | ||||
|   fileName: string, | ||||
|   lineNumber: number, | ||||
|   colNumber?: number, | ||||
| |}; | ||||
| export interface ErrorLocation { | ||||
|   fileName: string; | ||||
|   lineNumber: number; | ||||
|   colNumber?: number; | ||||
| } | ||||
| 
 | ||||
| const filePathRegex = /^\.(\/[^/\n ]+)+\.[^/\n ]+$/; | ||||
| 
 | ||||
|  | @ -22,11 +21,11 @@ const lineNumberRegexes = [ | |||
| 
 | ||||
| // Based on error formatting of webpack
 | ||||
| // https://github.com/webpack/webpack/blob/v3.5.5/lib/Stats.js#L183-L217
 | ||||
| function parseCompileError(message: string): ?ErrorLocation { | ||||
|   const lines: Array<string> = message.split('\n'); | ||||
|   let fileName: string = ''; | ||||
|   let lineNumber: number = 0; | ||||
|   let colNumber: number = 0; | ||||
| function parseCompileError(message: string): ErrorLocation | null | undefined { | ||||
|   const lines: string[] = message.split('\n'); | ||||
|   let fileName = ''; | ||||
|   let lineNumber = 0; | ||||
|   let colNumber = 0; | ||||
| 
 | ||||
|   for (let i = 0; i < lines.length; i++) { | ||||
|     const line: string = Anser.ansiToText(lines[i]).trim(); | ||||
|  | @ -34,13 +33,15 @@ function parseCompileError(message: string): ?ErrorLocation { | |||
|       continue; | ||||
|     } | ||||
| 
 | ||||
|     if (!fileName && line.match(filePathRegex)) { | ||||
|     if (!fileName && filePathRegex.exec(line)) { | ||||
|       fileName = line; | ||||
|     } | ||||
| 
 | ||||
|     let k = 0; | ||||
|     while (k < lineNumberRegexes.length) { | ||||
|       const match: ?Array<string> = line.match(lineNumberRegexes[k]); | ||||
|       const match: string[] | null | undefined = lineNumberRegexes[k].exec( | ||||
|         line | ||||
|       ); | ||||
|       if (match) { | ||||
|         lineNumber = parseInt(match[1], 10); | ||||
|         // colNumber starts with 0 and hence add 1
 | ||||
|  | @ -5,14 +5,13 @@ | |||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| /* @flow */ | ||||
| import StackFrame from './stack-frame'; | ||||
| 
 | ||||
| const regexExtractLocation = /\(?(.+?)(?::(\d+))?(?::(\d+))?\)?$/; | ||||
| 
 | ||||
| function extractLocation(token: string): [string, number, number] { | ||||
|   return regexExtractLocation | ||||
|     .exec(token) | ||||
|     .exec(token)! | ||||
|     .slice(1) | ||||
|     .map((v) => { | ||||
|       const p = Number(v); | ||||
|  | @ -20,7 +19,7 @@ function extractLocation(token: string): [string, number, number] { | |||
|         return p; | ||||
|       } | ||||
|       return v; | ||||
|     }); | ||||
|     }) as [string, number, number]; | ||||
| } | ||||
| 
 | ||||
| const regexValidFrame_Chrome = /^\s*(at|in)\s.+(:\d+)/; | ||||
|  | @ -46,7 +45,7 @@ function parseStack(stack: string[]): StackFrame[] { | |||
|         const last = data.pop(); | ||||
|         return new StackFrame( | ||||
|           data.join('@') || (isEval ? 'eval' : null), | ||||
|           ...extractLocation(last) | ||||
|           ...extractLocation(last!) | ||||
|         ); | ||||
|       } else { | ||||
|         // Strip eval, we don't care about it
 | ||||
|  | @ -58,7 +57,10 @@ function parseStack(stack: string[]): StackFrame[] { | |||
|         } | ||||
|         const data = e.trim().split(/\s+/g).slice(1); | ||||
|         const last = data.pop(); | ||||
|         return new StackFrame(data.join(' ') || null, ...extractLocation(last)); | ||||
|         return new StackFrame( | ||||
|           data.join(' ') || null, | ||||
|           ...extractLocation(last!) | ||||
|         ); | ||||
|       } | ||||
|     }); | ||||
|   return frames; | ||||
|  | @ -9,6 +9,7 @@ if (typeof Promise === 'undefined') { | |||
|   // Rejection tracking prevents a common issue where React gets into an
 | ||||
|   // inconsistent state due to an error, but it gets swallowed by a Promise,
 | ||||
|   // and the user has no idea what causes React's erratic future behavior.
 | ||||
|   // eslint-disable-next-line @typescript-eslint/no-var-requires
 | ||||
|   require('promise/lib/rejection-tracking').enable(); | ||||
|   window.Promise = require('promise/lib/es6-extensions.js'); | ||||
| } | ||||
|  | @ -5,8 +5,6 @@ | |||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| /* @flow */ | ||||
| 
 | ||||
| /** A container holding a script line. */ | ||||
| class ScriptLine { | ||||
|   /** The line number of this line of source. */ | ||||
|  | @ -16,7 +14,7 @@ class ScriptLine { | |||
|   /** Whether or not this line should be highlighted. Particularly useful for error reporting with context. */ | ||||
|   highlight: boolean; | ||||
| 
 | ||||
|   constructor(lineNumber: number, content: string, highlight: boolean = false) { | ||||
|   constructor(lineNumber: number, content: string, highlight = false) { | ||||
|     this.lineNumber = lineNumber; | ||||
|     this.content = content; | ||||
|     this.highlight = highlight; | ||||
|  | @ -98,10 +96,10 @@ class StackFrame { | |||
|       str += this.fileName + ':'; | ||||
|     } | ||||
|     if (this.lineNumber != null) { | ||||
|       str += this.lineNumber + ':'; | ||||
|       str += `${this.lineNumber}:`; | ||||
|     } | ||||
|     if (this.columnNumber != null) { | ||||
|       str += this.columnNumber + ':'; | ||||
|       str += `${this.columnNumber}:`; | ||||
|     } | ||||
|     return str.slice(0, -1); | ||||
|   } | ||||
|  | @ -5,7 +5,6 @@ | |||
|  * LICENSE file in the root directory of this source tree. | ||||
|  */ | ||||
| 
 | ||||
| /* @flow */ | ||||
| import StackFrame from './stack-frame'; | ||||
| import { getSourceMap } from './getSourceMap'; | ||||
| import { getLinesAround } from './getLinesAround'; | ||||
|  | @ -32,16 +31,16 @@ function count(search: string, string: string): number { | |||
|  * @param {number} [fileContents=3] The number of lines to provide before and after the line specified in the <code>StackFrame</code>. | ||||
|  */ | ||||
| async function unmap( | ||||
|   _fileUri: string | { uri: string, contents: string }, | ||||
|   _fileUri: string | { uri: string; contents: string }, | ||||
|   frames: StackFrame[], | ||||
|   contextLines: number = 3 | ||||
|   contextLines = 3 | ||||
| ): Promise<StackFrame[]> { | ||||
|   let fileContents = typeof _fileUri === 'object' ? _fileUri.contents : null; | ||||
|   let fileUri = typeof _fileUri === 'object' ? _fileUri.uri : _fileUri; | ||||
|   const fileUri = typeof _fileUri === 'object' ? _fileUri.uri : _fileUri; | ||||
|   if (fileContents == null) { | ||||
|     fileContents = await fetch(fileUri).then((res) => res.text()); | ||||
|   } | ||||
|   const map = await getSourceMap(fileUri, fileContents); | ||||
|   const map = await getSourceMap(fileUri, fileContents!); | ||||
|   return frames.map((frame) => { | ||||
|     const { | ||||
|       functionName, | ||||
|  | @ -104,7 +103,7 @@ async function unmap( | |||
|       sourceT, | ||||
|       lineNumber, | ||||
|       // $FlowFixMe
 | ||||
|       columnNumber | ||||
|       columnNumber! | ||||
|     ); | ||||
|     const originalSource = map.getSource(sourceT); | ||||
|     return new StackFrame( | ||||
|  | @ -0,0 +1,3 @@ | |||
| declare module 'settle-promise' { | ||||
|   export function settle(promises: Promise<void>[]): Promise<void>; | ||||
| } | ||||
|  | @ -1,55 +0,0 @@ | |||
| import React from 'react'; | ||||
| import { configure, mount } from 'enzyme'; | ||||
| import toJson from 'enzyme-to-json'; | ||||
| import StackTraceTab from '../src/StackTraceTab'; | ||||
| 
 | ||||
| import Adapter from 'enzyme-adapter-react-16'; | ||||
| configure({ adapter: new Adapter() }); | ||||
| 
 | ||||
| function genAsyncSnapshot(component, done) { | ||||
|   setTimeout(() => { | ||||
|     component.update(); | ||||
|     expect(toJson(component)).toMatchSnapshot(); | ||||
|     done(); | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| const actions = { | ||||
|   0: { type: 'PERFORM_ACTION', action: { type: '@@INIT' } }, | ||||
|   1: { type: 'PERFORM_ACTION', action: { type: 'INCREMENT_COUNTER' } }, | ||||
|   2: { | ||||
|     type: 'PERFORM_ACTION', | ||||
|     action: { type: 'INCREMENT_COUNTER' }, | ||||
|     stack: | ||||
|       'Error\n    at fn1 (app.js:72:24)\n    at fn2 (app.js:84:31)\n     ' + | ||||
|       'at fn3 (chrome-extension://lmhkpmbekcpmknklioeibfkpmmfibljd/js/page.bundle.js:1269:80)', | ||||
|   }, | ||||
| }; | ||||
| 
 | ||||
| describe('StackTraceTab component', () => { | ||||
|   it('should render with no props', (done) => { | ||||
|     const component = mount(<StackTraceTab />); | ||||
|     genAsyncSnapshot(component, done); | ||||
|   }); | ||||
| 
 | ||||
|   it('should render with props, but without stack', (done) => { | ||||
|     const component = mount( | ||||
|       <StackTraceTab actions={actions} action={actions[0].action} /> | ||||
|     ); | ||||
|     genAsyncSnapshot(component, done); | ||||
|   }); | ||||
| 
 | ||||
|   it('should render the link to docs', (done) => { | ||||
|     const component = mount( | ||||
|       <StackTraceTab actions={actions} action={actions[1].action} /> | ||||
|     ); | ||||
|     genAsyncSnapshot(component, done); | ||||
|   }); | ||||
| 
 | ||||
|   it('should render with trace stack', (done) => { | ||||
|     const component = mount( | ||||
|       <StackTraceTab actions={actions} action={actions[2].action} /> | ||||
|     ); | ||||
|     genAsyncSnapshot(component, done); | ||||
|   }); | ||||
| }); | ||||
|  | @ -0,0 +1,68 @@ | |||
| import React, { ReactComponentElement } from 'react'; | ||||
| import { configure, mount, ReactWrapper } from 'enzyme'; | ||||
| import toJson from 'enzyme-to-json'; | ||||
| import StackTraceTab from '../src/StackTraceTab'; | ||||
| 
 | ||||
| import Adapter from 'enzyme-adapter-react-16'; | ||||
| configure({ adapter: new Adapter() }); | ||||
| 
 | ||||
| function genAsyncSnapshot( | ||||
|   component: ReactWrapper<any, any, any>, | ||||
|   done: () => void | ||||
| ) { | ||||
|   setTimeout(() => { | ||||
|     component.update(); | ||||
|     expect(toJson(component)).toMatchSnapshot(); | ||||
|     done(); | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| const actions = { | ||||
|   0: { type: 'PERFORM_ACTION', action: { type: '@@INIT' } }, | ||||
|   1: { type: 'PERFORM_ACTION', action: { type: 'INCREMENT_COUNTER' } }, | ||||
|   2: { | ||||
|     type: 'PERFORM_ACTION', | ||||
|     action: { type: 'INCREMENT_COUNTER' }, | ||||
|     stack: | ||||
|       'Error\n    at fn1 (app.js:72:24)\n    at fn2 (app.js:84:31)\n     ' + | ||||
|       'at fn3 (chrome-extension://lmhkpmbekcpmknklioeibfkpmmfibljd/js/page.bundle.js:1269:80)', | ||||
|   }, | ||||
| }; | ||||
| 
 | ||||
| const StackTraceTabAsAny = StackTraceTab as any; | ||||
| 
 | ||||
| describe('StackTraceTab component', () => { | ||||
|   it('should render with no props', () => { | ||||
|     return new Promise((done) => { | ||||
|       const component = mount(<StackTraceTabAsAny />); | ||||
|       genAsyncSnapshot(component, done); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   it('should render with props, but without stack', () => { | ||||
|     return new Promise((done) => { | ||||
|       const component = mount( | ||||
|         <StackTraceTabAsAny actions={actions} action={actions[0].action} /> | ||||
|       ); | ||||
|       genAsyncSnapshot(component, done); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   it('should render the link to docs', () => { | ||||
|     return new Promise((done) => { | ||||
|       const component = mount( | ||||
|         <StackTraceTabAsAny actions={actions} action={actions[1].action} /> | ||||
|       ); | ||||
|       genAsyncSnapshot(component, done); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   it('should render with trace stack', () => { | ||||
|     return new Promise((done) => { | ||||
|       const component = mount( | ||||
|         <StackTraceTabAsAny actions={actions} action={actions[2].action} /> | ||||
|       ); | ||||
|       genAsyncSnapshot(component, done); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
|  | @ -301,12 +301,7 @@ exports[`StackTraceTab component should render with trace stack 1`] = ` | |||
|                       } | ||||
|                     } | ||||
|                   > | ||||
|                     <span | ||||
|                       onClick={null} | ||||
|                       onKeyDown={null} | ||||
|                       style={null} | ||||
|                       tabIndex={null} | ||||
|                     > | ||||
|                     <span> | ||||
|                       app.js:72:24 | ||||
|                     </span> | ||||
|                   </div> | ||||
|  | @ -345,12 +340,7 @@ exports[`StackTraceTab component should render with trace stack 1`] = ` | |||
|                       } | ||||
|                     } | ||||
|                   > | ||||
|                     <span | ||||
|                       onClick={null} | ||||
|                       onKeyDown={null} | ||||
|                       style={null} | ||||
|                       tabIndex={null} | ||||
|                     > | ||||
|                     <span> | ||||
|                       app.js:84:31 | ||||
|                     </span> | ||||
|                   </div> | ||||
							
								
								
									
										4
									
								
								packages/redux-devtools-trace-monitor/test/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								packages/redux-devtools-trace-monitor/test/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,4 @@ | |||
| { | ||||
|   "extends": "../../../tsconfig.react.base.json", | ||||
|   "include": ["../src", "."] | ||||
| } | ||||
							
								
								
									
										7
									
								
								packages/redux-devtools-trace-monitor/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								packages/redux-devtools-trace-monitor/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | |||
| { | ||||
|   "extends": "../../tsconfig.react.base.json", | ||||
|   "compilerOptions": { | ||||
|     "outDir": "lib" | ||||
|   }, | ||||
|   "include": ["src"] | ||||
| } | ||||
|  | @ -8,6 +8,6 @@ | |||
|     "esModuleInterop": true, | ||||
|     "forceConsistentCasingInFileNames": true, | ||||
|     // See https://github.com/DefinitelyTyped/DefinitelyTyped/issues/33311 | ||||
|     "types": ["node", "jest", "webpack-env"] | ||||
|     "types": ["node", "jest", "webpack-env", "chrome"] | ||||
|   } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										39
									
								
								yarn.lock
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								yarn.lock
									
									
									
									
									
								
							|  | @ -378,7 +378,7 @@ | |||
|     "@babel/helper-create-class-features-plugin" "^7.10.4" | ||||
|     "@babel/helper-plugin-utils" "^7.10.4" | ||||
| 
 | ||||
| "@babel/plugin-proposal-decorators@^7.10.5", "@babel/plugin-proposal-decorators@^7.8.3": | ||||
| "@babel/plugin-proposal-decorators@^7.8.3": | ||||
|   version "7.10.5" | ||||
|   resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.10.5.tgz#42898bba478bc4b1ae242a703a953a7ad350ffb4" | ||||
|   integrity sha512-Sc5TAQSZuLzgY0664mMDn24Vw2P8g/VhyLyGPaWiHahhgLqeZvcGeyBZOrJW0oSKIK2mvQ22a1ENXBIQLhrEiQ== | ||||
|  | @ -1110,7 +1110,7 @@ | |||
|     levenary "^1.1.1" | ||||
|     semver "^5.5.0" | ||||
| 
 | ||||
| "@babel/preset-flow@^7.0.0", "@babel/preset-flow@^7.10.4": | ||||
| "@babel/preset-flow@^7.0.0": | ||||
|   version "7.10.4" | ||||
|   resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.10.4.tgz#e0d9c72f8cb02d1633f6a5b7b16763aa2edf659f" | ||||
|   integrity sha512-XI6l1CptQCOBv+ZKYwynyswhtOKwpZZp5n0LG1QKCo8erRhqjoQV6nvx61Eg30JHpysWQSBwA2AWRU3pBbSY5g== | ||||
|  | @ -3251,6 +3251,11 @@ | |||
|   resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a" | ||||
|   integrity sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA== | ||||
| 
 | ||||
| "@types/babel__code-frame@^7.0.2": | ||||
|   version "7.0.2" | ||||
|   resolved "https://registry.yarnpkg.com/@types/babel__code-frame/-/babel__code-frame-7.0.2.tgz#e0c0f1648cbc09a9d4e5b4ed2ae9a6f7c8f5aeb0" | ||||
|   integrity sha512-imO+jT/yjOKOAS5GQZ8SDtwiIloAGGr6OaZDKB0V5JVaSfGZLat5K5/ZRtyKW6R60XHV3RHYPTFfhYb+wDKyKg== | ||||
| 
 | ||||
| "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7": | ||||
|   version "7.1.9" | ||||
|   resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.9.tgz#77e59d438522a6fb898fa43dc3455c6e72f3963d" | ||||
|  | @ -3309,6 +3314,14 @@ | |||
|   dependencies: | ||||
|     "@types/node" "*" | ||||
| 
 | ||||
| "@types/chrome@^0.0.124": | ||||
|   version "0.0.124" | ||||
|   resolved "https://registry.yarnpkg.com/@types/chrome/-/chrome-0.0.124.tgz#1cdb8e1c1ddb04b15844f5a71b9907f73bbb84a2" | ||||
|   integrity sha512-0UmDQ6A9gaahvztKryIonSTyUMEhuhKNyyJAnBB7ZJN/YXP7YRkL4onPFSTxnIbXcMnYsQFo8TxsGP8jY2mdEw== | ||||
|   dependencies: | ||||
|     "@types/filesystem" "*" | ||||
|     "@types/har-format" "*" | ||||
| 
 | ||||
| "@types/classnames@^2.2.10": | ||||
|   version "2.2.10" | ||||
|   resolved "https://registry.yarnpkg.com/@types/classnames/-/classnames-2.2.10.tgz#cc658ca319b6355399efc1f5b9e818f1a24bf999" | ||||
|  | @ -3419,6 +3432,18 @@ | |||
|     "@types/qs" "*" | ||||
|     "@types/serve-static" "*" | ||||
| 
 | ||||
| "@types/filesystem@*": | ||||
|   version "0.0.29" | ||||
|   resolved "https://registry.yarnpkg.com/@types/filesystem/-/filesystem-0.0.29.tgz#ee3748eb5be140dcf980c3bd35f11aec5f7a3748" | ||||
|   integrity sha512-85/1KfRedmfPGsbK8YzeaQUyV1FQAvMPMTuWFQ5EkLd2w7szhNO96bk3Rh/SKmOfd9co2rCLf0Voy4o7ECBOvw== | ||||
|   dependencies: | ||||
|     "@types/filewriter" "*" | ||||
| 
 | ||||
| "@types/filewriter@*": | ||||
|   version "0.0.28" | ||||
|   resolved "https://registry.yarnpkg.com/@types/filewriter/-/filewriter-0.0.28.tgz#c054e8af4d9dd75db4e63abc76f885168714d4b3" | ||||
|   integrity sha1-wFTor02d11205jq8dviFFocU1LM= | ||||
| 
 | ||||
| "@types/glob-base@^0.3.0": | ||||
|   version "0.3.0" | ||||
|   resolved "https://registry.yarnpkg.com/@types/glob-base/-/glob-base-0.3.0.tgz#a581d688347e10e50dd7c17d6f2880a10354319d" | ||||
|  | @ -3439,6 +3464,11 @@ | |||
|   dependencies: | ||||
|     "@types/node" "*" | ||||
| 
 | ||||
| "@types/har-format@*": | ||||
|   version "1.2.4" | ||||
|   resolved "https://registry.yarnpkg.com/@types/har-format/-/har-format-1.2.4.tgz#3275842095abb60d14b47fa798cc9ff708dab6d4" | ||||
|   integrity sha512-iUxzm1meBm3stxUMzRqgOVHjj4Kgpgu5w9fm4X7kPRfSgVRzythsucEN7/jtOo8SQzm+HfcxWWzJS0mJDH/3DQ== | ||||
| 
 | ||||
| "@types/hast@^2.0.0": | ||||
|   version "2.3.1" | ||||
|   resolved "https://registry.yarnpkg.com/@types/hast/-/hast-2.3.1.tgz#b16872f2a6144c7025f296fb9636a667ebb79cd9" | ||||
|  | @ -3464,6 +3494,11 @@ | |||
|     "@types/react" "*" | ||||
|     hoist-non-react-statics "^3.3.0" | ||||
| 
 | ||||
| "@types/html-entities@^1.2.16": | ||||
|   version "1.2.16" | ||||
|   resolved "https://registry.yarnpkg.com/@types/html-entities/-/html-entities-1.2.16.tgz#4d1fe208c4c33727ac4657e6f5d92bfe52427023" | ||||
|   integrity sha512-CI6fHfFvkTtX2Nlr4JBA6yIFTfA4p9E6w9ky64X6PrfXiTALhUh/SOa+Sxvv2p87m+y5AH71lAUrx0lSYx4hKQ== | ||||
| 
 | ||||
| "@types/html-minifier-terser@^5.0.0": | ||||
|   version "5.1.0" | ||||
|   resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.1.0.tgz#551a4589b6ee2cc9c1dff08056128aec29b94880" | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user