diff --git a/.eslintrc.js b/.eslintrc.js
index b2f77e5..e3a49a6 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -12,6 +12,7 @@ module.exports = {
"plugin:import/warnings",
"plugin:prettier/recommended",
"prettier/react",
+ "plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
],
globals: {
@@ -21,6 +22,8 @@ module.exports = {
ignorePatterns: ["node_modules/"],
parser: '@typescript-eslint/parser',
parserOptions: {
+ project: "tsconfig.json",
+ tsconfigRootDir: ".",
ecmaFeatures: {
jsx: true,
},
diff --git a/config/webpack.dev.config.js b/config/webpack.dev.config.js
index 9607242..22488f7 100644
--- a/config/webpack.dev.config.js
+++ b/config/webpack.dev.config.js
@@ -15,7 +15,7 @@ var srcDir = path.join(appDir, 'src');
var options = {
entry: {
background: path.join(srcDir, 'background.js'),
- inspector: path.join(srcDir, 'inspector.js'),
+ inspector: path.join(srcDir, 'inspector.tsx'),
},
output: {
path: path.join(appDir, 'build'),
diff --git a/config/webpack.prod.config.js b/config/webpack.prod.config.js
index 4693018..d36f4c7 100644
--- a/config/webpack.prod.config.js
+++ b/config/webpack.prod.config.js
@@ -15,7 +15,7 @@ var srcDir = path.join(appDir, "src");
var options = {
entry: {
background: path.join(srcDir, "background.js"),
- inspector: path.join(srcDir, "inspector.js"),
+ inspector: path.join(srcDir, "inspector.tsx"),
},
output: {
path: path.join(appDir, "build"),
diff --git a/package-lock.json b/package-lock.json
index e054b57..eb788a3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3148,6 +3148,15 @@
"to-fast-properties": "^2.0.0"
}
},
+ "@types/chrome": {
+ "version": "0.0.92",
+ "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.92.tgz",
+ "integrity": "sha512-bTv1EljZ03bexRJwS5FwSZmrudtw+QNbzwUY2sxVtXWgtxk752G4I2owhZ+Mlzbf3VKvG+rBYSw/FnvzuZ4xOA==",
+ "dev": true,
+ "requires": {
+ "@types/filesystem": "*"
+ }
+ },
"@types/classnames": {
"version": "2.2.9",
"resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.9.tgz",
@@ -3160,6 +3169,21 @@
"integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==",
"dev": true
},
+ "@types/filesystem": {
+ "version": "0.0.29",
+ "resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.29.tgz",
+ "integrity": "sha512-85/1KfRedmfPGsbK8YzeaQUyV1FQAvMPMTuWFQ5EkLd2w7szhNO96bk3Rh/SKmOfd9co2rCLf0Voy4o7ECBOvw==",
+ "dev": true,
+ "requires": {
+ "@types/filewriter": "*"
+ }
+ },
+ "@types/filewriter": {
+ "version": "0.0.28",
+ "resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.28.tgz",
+ "integrity": "sha1-wFTor02d11205jq8dviFFocU1LM=",
+ "dev": true
+ },
"@types/json-schema": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz",
diff --git a/package.json b/package.json
index b2eafc2..5d52538 100644
--- a/package.json
+++ b/package.json
@@ -5,6 +5,7 @@
"scripts": {
"build": "node scripts/build.js",
"start": "node scripts/webserver.js",
+ "eslint": "eslint --cache --ext .js,.jsx,.ts,.tsx src",
"es": "eslint ./src "
},
"devDependencies": {
@@ -14,6 +15,7 @@
"@babel/preset-env": "^7.8.3",
"@babel/preset-react": "^7.7.4",
"@babel/preset-typescript": "^7.8.3",
+ "@types/chrome": "0.0.92",
"@types/classnames": "^2.2.9",
"@types/react": "^16.9.17",
"@types/react-dom": "^16.9.4",
diff --git a/src/inspector.js b/src/inspector.tsx
similarity index 87%
rename from src/inspector.js
rename to src/inspector.tsx
index 30f0759..f47c9f8 100644
--- a/src/inspector.js
+++ b/src/inspector.tsx
@@ -1,14 +1,15 @@
+///
import React from 'react';
import ReactDOM from 'react-dom';
import './reset.css';
import App from './viewer/App';
const tabId = parseInt(window.location.search.substr(1), 10);
-
-const handlers = {};
+// TODO create
+const handlers: any = {};
function startDebugging() {
- chrome.debugger.sendCommand({ tabId }, 'Network.enable', null, () => {
+ chrome.debugger.sendCommand({ tabId }, 'Network.enable', undefined, () => {
if (chrome.runtime.lastError) {
console.error(chrome.runtime.lastError.message);
} else {
diff --git a/src/viewer/App.js b/src/viewer/App.tsx
similarity index 70%
rename from src/viewer/App.js
rename to src/viewer/App.tsx
index 11c7425..37be7cd 100644
--- a/src/viewer/App.js
+++ b/src/viewer/App.tsx
@@ -1,21 +1,34 @@
-import React from 'react';
+import React, { ChangeEvent } from 'react';
import Panel from 'react-flex-panel';
import ControlPanel from './ControlPanel/ControlPanel';
import FrameList from './FrameTable/FrameTable';
-import FrameView from './PanelView/PanelView';
+import PanelView from './PanelView/PanelView';
import { stringToBuffer } from './Helpers/Helper';
import './App.scss';
+import { IFrame, IFrameType, Response } from './types';
-export default class App extends React.Component {
+interface AppProps {
+ handlers: any;
+}
+interface AppState {
+ [key: string]: any;
+ frames: IFrame[];
+ activeId: number | null;
+ isCapturing: boolean;
+ regName: string;
+ filter: string;
+ isFilterInverse: boolean;
+}
+export default class App extends React.Component {
frameUniqueId = 0;
- frameIssueTime = null;
+ frameIssueTime: number;
- frameIssueWallTime = null;
+ frameIssueWallTime: number;
cacheKey = ['isCapturing', 'regName', 'filter', 'isFilterInverse'];
- constructor(props) {
+ constructor(props: AppProps) {
super(props);
this.props.handlers['Network.webSocketFrameReceived'] = this.webSocketFrameReceived.bind(this);
this.props.handlers['Network.webSocketFrameSent'] = this.webSocketFrameSent.bind(this);
@@ -30,10 +43,16 @@ export default class App extends React.Component {
}
componentDidMount() {
- const cacheState = this.cacheKey.reduce((acc, key) => {
- const value = localStorage.getItem(key);
+ const cacheState = this.cacheKey.reduce>((acc, key) => {
+ const value = window.localStorage.getItem(key);
if (value !== null && value !== undefined) {
acc[key] = value;
+ if (value === 'true') {
+ acc[key] = true;
+ }
+ if (value === 'false') {
+ acc[key] = false;
+ }
}
return acc;
}, {});
@@ -41,7 +60,7 @@ export default class App extends React.Component {
// TODO Boolean values turns to strings.
}
- componentDidUpdate(prevProps, prevState) {
+ componentDidUpdate(prevProps: AppProps, prevState: AppState) {
this.cacheKey.forEach((key) => {
if (prevState[key] !== this.state[key]) {
localStorage.setItem(key, this.state[key]);
@@ -49,7 +68,7 @@ export default class App extends React.Component {
});
}
- getTime(timestamp) {
+ getTime(timestamp: number) {
if (this.frameIssueTime == null) {
this.frameIssueTime = timestamp;
this.frameIssueWallTime = new Date().getTime();
@@ -85,7 +104,7 @@ export default class App extends React.Component {
{active != null ? (
-
+
) : (
Select a frame to view its contents
)}
@@ -94,7 +113,7 @@ export default class App extends React.Component {
);
}
- selectFrame = (id) => {
+ selectFrame = (id: number) => {
this.setState({ activeId: id });
};
@@ -106,11 +125,11 @@ export default class App extends React.Component {
this.setState({ isCapturing: !this.state.isCapturing });
};
- setRegName = (e) => {
+ setRegName = (e: ChangeEvent) => {
this.setState({ regName: e.target.value });
};
- setFilter = (e) => {
+ setFilter = (e: ChangeEvent) => {
this.setState({ filter: e.target.value });
};
@@ -118,9 +137,9 @@ export default class App extends React.Component {
this.setState({ isFilterInverse: !this.state.isFilterInverse });
};
- addFrame(type, timestamp, response) {
+ addFrame(type: IFrameType, timestamp: number, response: Response) {
if (response.opcode === 1 || response.opcode === 2) {
- const frame = {
+ const frame: IFrame = {
type,
name: type,
id: this.frameUniqueId,
@@ -137,16 +156,14 @@ export default class App extends React.Component {
}
}
- webSocketFrameReceived({ timestamp, response }) {
- if (this.state.isCapturing === true || this.state.isCapturing === 'true') {
- // see componentDidMount()
+ webSocketFrameReceived({ timestamp, response }: { timestamp: number; response: Response }) {
+ if (this.state.isCapturing) {
this.addFrame('incoming', timestamp, response);
}
}
- webSocketFrameSent({ timestamp, response }) {
- if (this.state.isCapturing === true || this.state.isCapturing === 'true') {
- // see componentDidMount()
+ webSocketFrameSent({ timestamp, response }: { timestamp: number; response: Response }) {
+ if (this.state.isCapturing) {
this.addFrame('outgoing', timestamp, response);
}
}
diff --git a/src/viewer/ControlPanel/ControlPanel.tsx b/src/viewer/ControlPanel/ControlPanel.tsx
index fe7db61..17d5699 100644
--- a/src/viewer/ControlPanel/ControlPanel.tsx
+++ b/src/viewer/ControlPanel/ControlPanel.tsx
@@ -1,23 +1,24 @@
-import React, { ChangeEvent } from 'react';
+import React, { ChangeEvent, MouseEvent } from 'react';
import FontAwesome from 'react-fontawesome';
import cx from 'classnames';
import './ControlPanel.scss';
import { EFilter } from '../types';
-
-type ControlPanelProps = {
+// TODO not to return events
+type ControlPanelMode = {
isCapturing: boolean;
- onClear: { (event: MouseEvent): void };
- onFilterModeToggle: { (event: MouseEvent): void };
- onCapturingToggle: { (event: MouseEvent): void };
- onRegName: { (event: ChangeEvent): void };
- onFilter: { (event: ChangeEvent): void };
+ onClear: (event: MouseEvent) => void;
+ onFilterModeToggle: (event: MouseEvent) => void;
+ onCapturingToggle: (event: MouseEvent) => void;
+ onRegName: (event: ChangeEvent) => void;
+ onFilter: (event: ChangeEvent) => void;
};
-interface EProps extends ControlPanelProps, EFilter {}
-interface EState {
+interface ControlPanelProps extends ControlPanelMode, EFilter {}
+interface ControlPanelState {
openInput?: null | 'filter' | 'name';
}
-export default class ControlPanel extends React.Component {
- constructor(props: EProps) {
+
+export default class ControlPanel extends React.Component {
+ constructor(props: ControlPanelProps) {
super(props);
this.state = {
openInput: null, // 'filter' | 'name'
diff --git a/src/viewer/FrameTable/FrameTable.tsx b/src/viewer/FrameTable/FrameTable.tsx
index f902867..64afea5 100644
--- a/src/viewer/FrameTable/FrameTable.tsx
+++ b/src/viewer/FrameTable/FrameTable.tsx
@@ -1,23 +1,15 @@
/* eslint max-classes-per-file: 0 */
-import React from 'react';
+import React, { MouseEvent } from 'react';
import cx from 'classnames';
import FontAwesome from 'react-fontawesome';
import { checkViable, getName, TimeStamp } from '../Helpers/Helper';
import './FrameTable.scss';
import { EFilter, IFrame } from '../types';
-class Filter implements EFilter {
- public regName: RegExp;
- public filter: RegExp;
- public isFilterInverse: boolean;
-}
-type FrameListProps = { frames: IFrame[]; activeId: number; onSelect: any };
-type FrameEntryProps = {
- key: number;
- frame: IFrame;
- selected: boolean;
- onSelect: void;
- filterData: EFilter;
+type FrameListProps = {
+ frames: IFrame[];
+ activeId: number | null;
+ onSelect: (id: number | null) => void;
};
export default class FrameList extends React.Component {
@@ -43,11 +35,20 @@ export default class FrameList extends React.Component
}
}
+type FrameEntryProps = {
+ key: number;
+ frame: IFrame;
+ selected: boolean;
+ onSelect: (id: number | null) => void;
+ filterData: EFilter;
+};
+
class FrameEntry extends React.PureComponent {
- handlerSelect = (e) => {
+ handlerSelect = (e: MouseEvent) => {
e.stopPropagation();
this.props.onSelect(this.props.frame.id);
};
+
render() {
const { frame, selected, filterData } = this.props;
if (checkViable(frame, filterData as EFilter)) return null;
diff --git a/src/viewer/Helpers/Helper.ts b/src/viewer/Helpers/Helper.ts
index ce6d920..35f0e05 100644
--- a/src/viewer/Helpers/Helper.ts
+++ b/src/viewer/Helpers/Helper.ts
@@ -32,7 +32,7 @@ export const getName = (frame: IFrame, filterData: EFilter): string => {
};
export const checkViable = (frame: IFrame, filterData: EFilter): boolean => {
- if (filterData.filter) {
+ if (filterData.filter && frame.text) {
if (filterData.isFilterInverse) {
return !!grep(frame.text, filterData.filter);
}
diff --git a/src/viewer/PanelView/HexViewer.js b/src/viewer/PanelView/HexViewer.tsx
similarity index 91%
rename from src/viewer/PanelView/HexViewer.js
rename to src/viewer/PanelView/HexViewer.tsx
index 7e25288..a0de4b7 100644
--- a/src/viewer/PanelView/HexViewer.js
+++ b/src/viewer/PanelView/HexViewer.tsx
@@ -2,7 +2,11 @@ import React from 'react';
import classNames from 'classnames';
import './HexViewer.scss';
-export default function HexViewer(props) {
+interface HexViewerProps {
+ className: string;
+ data: Uint8Array;
+}
+export default function HexViewer(props: HexViewerProps) {
const { data, className } = props;
let numDigits = 4;
/* eslint-disable-next-line no-bitwise */
diff --git a/src/viewer/PanelView/PanelView.tsx b/src/viewer/PanelView/PanelView.tsx
index a92099f..7f52f13 100644
--- a/src/viewer/PanelView/PanelView.tsx
+++ b/src/viewer/PanelView/PanelView.tsx
@@ -1,22 +1,31 @@
-import { ObjectInspector } from 'react-inspector';
+import { ObjectInspector, ObjectInspectorProps } from 'react-inspector';
import React from 'react';
import cx from 'classnames';
import HexViewer from './HexViewer';
import './PanelView.scss';
+import { IFrame } from '../types';
-const TextViewer = ({ data }) => {data}
;
+const TextViewer = ({ data }: { data: string | undefined }) => (
+ {data}
+);
-const JsonViewer = ({ data }) => (
+const JsonViewer = (data: ObjectInspectorProps) => (
-
+
);
+type PanelName = 'json' | 'hex' | 'text';
+interface PanelViewState {
+ panel?: PanelName | PanelName[] | null;
+}
+interface PanelViewProps {
+ frame: IFrame;
+}
-
-export default class FrameView extends React.Component {
+export default class PanelView extends React.Component {
state = { panel: null };
- static getDerivedStateFromProps(props, state) {
+ static getDerivedStateFromProps(props: PanelViewProps, state: PanelViewState) {
const { frame } = props;
const panels = [];
if (frame.binary) {
@@ -39,13 +48,13 @@ export default class FrameView extends React.Component {
panels.push('text');
}
- if (!panels.includes(state.panel)) {
+ if (!panels.includes(state.panel as string)) {
return { panel: panels[0] };
}
return null;
}
- makePanel(name, title) {
+ makePanel(name: PanelName, title: string) {
return (
{panel === 'text' && }
{panel === 'json' && }
- {panel === 'hex' && }
+ {panel === 'hex' && }
);
}
diff --git a/src/viewer/types.ts b/src/viewer/types.ts
index 6268a84..9b36ddb 100644
--- a/src/viewer/types.ts
+++ b/src/viewer/types.ts
@@ -1,4 +1,4 @@
-type IFrameType = 'incoming' | 'outgoing';
+export type IFrameType = 'incoming' | 'outgoing';
export type IFrame = {
type: IFrameType;
name: string;
@@ -6,11 +6,16 @@ export type IFrame = {
time: Date;
length: number;
text?: string;
- binary?: Uint8Array; //TODO string to buffer type
+ binary?: Uint8Array;
+ json?: object;
};
export interface EFilter {
regName: string;
- filter: ;
+ filter: string;
isFilterInverse: boolean;
}
+export interface Response {
+ opcode: number;
+ payloadData: string;
+}
diff --git a/tsconfig.json b/tsconfig.json
index 1195e2c..6a5efee 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,7 +1,7 @@
{
"compilerOptions": {
- "lib": ["es2018", "es2020.string"],
- "strictNullChecks": false, // TODO change before release
+ "lib": ["es2018", "es2020.string", "DOM"],
+ "strictNullChecks": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"module": "ESNext",