From ee52c29a8d8053d57906a8254d079490dee07a20 Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Sun, 25 Oct 2020 19:32:04 -0400 Subject: [PATCH] chore(core): convert to TypeScript (#655) * Get started * stash * stash * stash * stash * stash * stash * stash * stash * stash * stash * stash * stash * stash * stash * stash * stash --- .../d3-state-visualizer/src/charts/index.ts | 1 + .../src/charts/tree/tree.ts | 4 +- packages/d3-state-visualizer/src/index.ts | 1 + packages/devui/src/Tabs/index.ts | 1 + packages/devui/src/index.ts | 3 +- packages/devui/src/utils/theme.ts | 7 +- .../src/Chart.tsx | 2 + .../src/ChartMonitor.tsx | 2 + packages/redux-devtools-core/.babelrc | 6 +- packages/redux-devtools-core/.eslintignore | 2 + packages/redux-devtools-core/.eslintrc.js | 29 + packages/redux-devtools-core/jest.config.js | 4 + packages/redux-devtools-core/package.json | 100 +-- .../src/app/actions/index.js | 122 ---- .../src/app/actions/index.ts | 573 ++++++++++++++++++ .../{BottomButtons.js => BottomButtons.tsx} | 11 +- .../app/components/{Header.js => Header.tsx} | 27 +- .../src/app/components/InstanceSelector.js | 48 -- .../src/app/components/InstanceSelector.tsx | 43 ++ .../src/app/components/MonitorSelector.js | 45 -- .../src/app/components/MonitorSelector.tsx | 39 ++ .../{Connection.js => Connection.tsx} | 109 ++-- .../Settings/{Themes.js => Themes.tsx} | 32 +- .../src/app/components/Settings/index.js | 32 - .../src/app/components/Settings/index.tsx | 34 ++ .../{TopButtons.js => TopButtons.tsx} | 15 +- ...spatcherButton.js => DispatcherButton.tsx} | 27 +- .../app/components/buttons/ExportButton.js | 33 - .../app/components/buttons/ExportButton.tsx | 28 + .../app/components/buttons/ImportButton.js | 64 -- .../app/components/buttons/ImportButton.tsx | 54 ++ .../buttons/{LockButton.js => LockButton.tsx} | 24 +- .../{PersistButton.js => PersistButton.tsx} | 38 +- .../{PrintButton.js => PrintButton.tsx} | 14 +- .../{RecordButton.js => RecordButton.tsx} | 22 +- .../{SliderButton.js => SliderButton.tsx} | 27 +- .../src/app/components/buttons/SyncButton.js | 45 -- .../src/app/components/buttons/SyncButton.tsx | 39 ++ .../{actionTypes.js => actionTypes.ts} | 0 .../constants/{dataTypes.js => dataTypes.ts} | 0 ...ketActionTypes.js => socketActionTypes.ts} | 11 +- .../{socketOptions.js => socketOptions.ts} | 0 .../containers/{Actions.js => Actions.tsx} | 37 +- .../src/app/containers/{App.js => App.tsx} | 43 +- .../containers/{DevTools.js => DevTools.tsx} | 67 +- ...itorWrapper.js => ChartMonitorWrapper.tsx} | 35 +- .../{Dispatcher.js => Dispatcher.tsx} | 62 +- .../{ChartTab.js => ChartTab.tsx} | 66 +- .../{RawTab.js => RawTab.tsx} | 16 +- .../{SubTabs.js => SubTabs.tsx} | 59 +- .../{VisualDiffTab.js => VisualDiffTab.tsx} | 21 +- .../InspectorWrapper/{index.js => index.tsx} | 33 +- .../monitors/{Slider.js => Slider.tsx} | 23 +- .../src/app/{index.js => index.tsx} | 27 +- .../src/app/middlewares/{api.js => api.ts} | 104 +++- .../{exportState.js => exportState.ts} | 19 +- .../reducers/{connection.js => connection.ts} | 15 +- .../src/app/reducers/index.js | 22 - .../src/app/reducers/index.ts | 34 ++ .../reducers/{instances.js => instances.ts} | 103 +++- .../app/reducers/{monitor.js => monitor.ts} | 36 +- .../{notification.js => notification.ts} | 12 +- .../app/reducers/{reports.js => reports.ts} | 21 +- .../src/app/reducers/section.js | 8 - .../src/app/reducers/section.ts | 11 + .../src/app/reducers/{socket.js => socket.ts} | 25 +- .../src/app/reducers/theme.js | 15 - .../src/app/reducers/theme.ts | 27 + .../src/app/store/configureStore.js | 41 -- .../src/app/store/configureStore.ts | 60 ++ ...xcessActions.js => commitExcessActions.ts} | 4 +- .../utils/{getMonitor.js => getMonitor.tsx} | 2 +- .../{monitorActions.js => monitorActions.ts} | 18 +- .../app/utils/{parseJSON.js => parseJSON.ts} | 15 +- .../src/app/utils/stringifyJSON.js | 20 - .../src/app/utils/stringifyJSON.ts | 24 + .../utils/{updateState.js => updateState.ts} | 21 +- packages/redux-devtools-core/src/index.js | 8 - .../{index.js => src/index.tsx} | 2 +- .../src/utils/catchErrors.js | 42 -- .../src/utils/catchErrors.ts | 68 +++ .../src/utils/{filters.js => filters.ts} | 92 ++- .../src/utils/get-params.ts | 4 + .../src/utils/importState.js | 69 --- .../src/utils/importState.ts | 105 ++++ .../src/utils/{index.js => index.ts} | 130 +++- .../test/__mocks__/styleMock.js | 1 - .../test/{app.spec.js => app.spec.tsx} | 6 +- .../test/{setup.js => setup.ts} | 0 .../redux-devtools-core/test/tsconfig.json | 4 + packages/redux-devtools-core/tsconfig.json | 7 + .../redux-devtools-core/tsconfig.webpack.json | 4 + .../{webpack.config.js => webpack.config.ts} | 23 +- ...ck.config.umd.js => webpack.config.umd.ts} | 19 +- .../src/ActionPreview.tsx | 4 +- .../src/DevtoolsInspector.tsx | 6 +- .../src/redux.ts | 2 +- .../src/tabs/JSONDiff.tsx | 2 +- .../src/tabs/getItemString.tsx | 2 +- .../src/immutable/index.ts | 1 + .../redux-devtools-serialize/src/index.ts | 9 +- yarn.lock | 114 +++- 102 files changed, 2352 insertions(+), 1236 deletions(-) create mode 100644 packages/redux-devtools-core/.eslintignore create mode 100644 packages/redux-devtools-core/.eslintrc.js create mode 100644 packages/redux-devtools-core/jest.config.js delete mode 100644 packages/redux-devtools-core/src/app/actions/index.js create mode 100644 packages/redux-devtools-core/src/app/actions/index.ts rename packages/redux-devtools-core/src/app/components/{BottomButtons.js => BottomButtons.tsx} (85%) rename packages/redux-devtools-core/src/app/components/{Header.js => Header.tsx} (77%) delete mode 100644 packages/redux-devtools-core/src/app/components/InstanceSelector.js create mode 100644 packages/redux-devtools-core/src/app/components/InstanceSelector.tsx delete mode 100644 packages/redux-devtools-core/src/app/components/MonitorSelector.js create mode 100644 packages/redux-devtools-core/src/app/components/MonitorSelector.tsx rename packages/redux-devtools-core/src/app/components/Settings/{Connection.js => Connection.tsx} (55%) rename packages/redux-devtools-core/src/app/components/Settings/{Themes.js => Themes.tsx} (64%) delete mode 100644 packages/redux-devtools-core/src/app/components/Settings/index.js create mode 100644 packages/redux-devtools-core/src/app/components/Settings/index.tsx rename packages/redux-devtools-core/src/app/components/{TopButtons.js => TopButtons.tsx} (84%) rename packages/redux-devtools-core/src/app/components/buttons/{DispatcherButton.js => DispatcherButton.tsx} (53%) delete mode 100644 packages/redux-devtools-core/src/app/components/buttons/ExportButton.js create mode 100644 packages/redux-devtools-core/src/app/components/buttons/ExportButton.tsx delete mode 100644 packages/redux-devtools-core/src/app/components/buttons/ImportButton.js create mode 100644 packages/redux-devtools-core/src/app/components/buttons/ImportButton.tsx rename packages/redux-devtools-core/src/app/components/buttons/{LockButton.js => LockButton.tsx} (60%) rename packages/redux-devtools-core/src/app/components/buttons/{PersistButton.js => PersistButton.tsx} (52%) rename packages/redux-devtools-core/src/app/components/buttons/{PrintButton.js => PrintButton.tsx} (72%) rename packages/redux-devtools-core/src/app/components/buttons/{RecordButton.js => RecordButton.tsx} (61%) rename packages/redux-devtools-core/src/app/components/buttons/{SliderButton.js => SliderButton.tsx} (51%) delete mode 100644 packages/redux-devtools-core/src/app/components/buttons/SyncButton.js create mode 100644 packages/redux-devtools-core/src/app/components/buttons/SyncButton.tsx rename packages/redux-devtools-core/src/app/constants/{actionTypes.js => actionTypes.ts} (100%) rename packages/redux-devtools-core/src/app/constants/{dataTypes.js => dataTypes.ts} (100%) rename packages/redux-devtools-core/src/app/constants/{socketActionTypes.js => socketActionTypes.ts} (78%) rename packages/redux-devtools-core/src/app/constants/{socketOptions.js => socketOptions.ts} (100%) rename packages/redux-devtools-core/src/app/containers/{Actions.js => Actions.tsx} (72%) rename packages/redux-devtools-core/src/app/containers/{App.js => App.tsx} (54%) rename packages/redux-devtools-core/src/app/containers/{DevTools.js => DevTools.tsx} (53%) rename packages/redux-devtools-core/src/app/containers/monitors/{ChartMonitorWrapper.js => ChartMonitorWrapper.tsx} (56%) rename packages/redux-devtools-core/src/app/containers/monitors/{Dispatcher.js => Dispatcher.tsx} (77%) rename packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/{ChartTab.js => ChartTab.tsx} (53%) rename packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/{RawTab.js => RawTab.tsx} (57%) rename packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/{SubTabs.js => SubTabs.tsx} (62%) rename packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/{VisualDiffTab.js => VisualDiffTab.tsx} (93%) rename packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/{index.js => index.tsx} (58%) rename packages/redux-devtools-core/src/app/containers/monitors/{Slider.js => Slider.tsx} (58%) rename packages/redux-devtools-core/src/app/{index.js => index.tsx} (58%) rename packages/redux-devtools-core/src/app/middlewares/{api.js => api.ts} (70%) rename packages/redux-devtools-core/src/app/middlewares/{exportState.js => exportState.ts} (72%) rename packages/redux-devtools-core/src/app/reducers/{connection.js => connection.ts} (50%) delete mode 100644 packages/redux-devtools-core/src/app/reducers/index.js create mode 100644 packages/redux-devtools-core/src/app/reducers/index.ts rename packages/redux-devtools-core/src/app/reducers/{instances.js => instances.ts} (76%) rename packages/redux-devtools-core/src/app/reducers/{monitor.js => monitor.ts} (62%) rename packages/redux-devtools-core/src/app/reducers/{notification.js => notification.ts} (57%) rename packages/redux-devtools-core/src/app/reducers/{reports.js => reports.ts} (67%) delete mode 100644 packages/redux-devtools-core/src/app/reducers/section.js create mode 100644 packages/redux-devtools-core/src/app/reducers/section.ts rename packages/redux-devtools-core/src/app/reducers/{socket.js => socket.ts} (72%) delete mode 100644 packages/redux-devtools-core/src/app/reducers/theme.js create mode 100644 packages/redux-devtools-core/src/app/reducers/theme.ts delete mode 100644 packages/redux-devtools-core/src/app/store/configureStore.js create mode 100644 packages/redux-devtools-core/src/app/store/configureStore.ts rename packages/redux-devtools-core/src/app/utils/{commitExcessActions.js => commitExcessActions.ts} (89%) rename packages/redux-devtools-core/src/app/utils/{getMonitor.js => getMonitor.tsx} (90%) rename packages/redux-devtools-core/src/app/utils/{monitorActions.js => monitorActions.ts} (78%) rename packages/redux-devtools-core/src/app/utils/{parseJSON.js => parseJSON.ts} (70%) delete mode 100644 packages/redux-devtools-core/src/app/utils/stringifyJSON.js create mode 100644 packages/redux-devtools-core/src/app/utils/stringifyJSON.ts rename packages/redux-devtools-core/src/app/utils/{updateState.js => updateState.ts} (66%) delete mode 100644 packages/redux-devtools-core/src/index.js rename packages/redux-devtools-core/{index.js => src/index.tsx} (94%) delete mode 100644 packages/redux-devtools-core/src/utils/catchErrors.js create mode 100644 packages/redux-devtools-core/src/utils/catchErrors.ts rename packages/redux-devtools-core/src/utils/{filters.js => filters.ts} (55%) create mode 100644 packages/redux-devtools-core/src/utils/get-params.ts delete mode 100644 packages/redux-devtools-core/src/utils/importState.js create mode 100644 packages/redux-devtools-core/src/utils/importState.ts rename packages/redux-devtools-core/src/utils/{index.js => index.ts} (55%) delete mode 100644 packages/redux-devtools-core/test/__mocks__/styleMock.js rename packages/redux-devtools-core/test/{app.spec.js => app.spec.tsx} (92%) rename packages/redux-devtools-core/test/{setup.js => setup.ts} (100%) create mode 100644 packages/redux-devtools-core/test/tsconfig.json create mode 100644 packages/redux-devtools-core/tsconfig.json create mode 100644 packages/redux-devtools-core/tsconfig.webpack.json rename packages/redux-devtools-core/{webpack.config.js => webpack.config.ts} (72%) rename packages/redux-devtools-core/{webpack.config.umd.js => webpack.config.umd.ts} (74%) diff --git a/packages/d3-state-visualizer/src/charts/index.ts b/packages/d3-state-visualizer/src/charts/index.ts index 33abb024..aaf238e0 100644 --- a/packages/d3-state-visualizer/src/charts/index.ts +++ b/packages/d3-state-visualizer/src/charts/index.ts @@ -1 +1,2 @@ export { default as tree } from './tree/tree'; +export type { InputOptions, NodeWithId } from './tree/tree'; diff --git a/packages/d3-state-visualizer/src/charts/tree/tree.ts b/packages/d3-state-visualizer/src/charts/tree/tree.ts index 894eceb4..47ae5176 100644 --- a/packages/d3-state-visualizer/src/charts/tree/tree.ts +++ b/packages/d3-state-visualizer/src/charts/tree/tree.ts @@ -10,7 +10,7 @@ import { } from './utils'; import d3tooltip from 'd3tooltip'; -interface InputOptions { +export interface InputOptions { // eslint-disable-next-line @typescript-eslint/ban-types state?: {} | null; // eslint-disable-next-line @typescript-eslint/ban-types @@ -34,7 +34,7 @@ interface InputOptions { widthBetweenNodesCoeff: number; transitionDuration: number; blinkDuration: number; - onClickText: () => void; + onClickText: (datum: NodeWithId) => void; tooltipOptions: { disabled?: boolean; left?: number | undefined; diff --git a/packages/d3-state-visualizer/src/index.ts b/packages/d3-state-visualizer/src/index.ts index 78a37ac1..60b538ee 100644 --- a/packages/d3-state-visualizer/src/index.ts +++ b/packages/d3-state-visualizer/src/index.ts @@ -1,5 +1,6 @@ import * as charts from './charts'; export { tree } from './charts'; +export type { InputOptions, NodeWithId } from './charts'; export default charts; diff --git a/packages/devui/src/Tabs/index.ts b/packages/devui/src/Tabs/index.ts index bc6749b1..f58f9e2c 100644 --- a/packages/devui/src/Tabs/index.ts +++ b/packages/devui/src/Tabs/index.ts @@ -1 +1,2 @@ export { default } from './Tabs'; +export { Tab } from './TabsHeader'; diff --git a/packages/devui/src/index.ts b/packages/devui/src/index.ts index cf2dfa86..eabef90a 100644 --- a/packages/devui/src/index.ts +++ b/packages/devui/src/index.ts @@ -6,7 +6,7 @@ export { default as Editor } from './Editor'; export { default as Form } from './Form'; export { default as Select } from './Select'; export { default as Slider } from './Slider'; -export { default as Tabs } from './Tabs'; +export { default as Tabs, Tab } from './Tabs'; export { default as SegmentedControl } from './SegmentedControl'; export { default as Notification } from './Notification'; export * from './Toolbar'; @@ -14,3 +14,4 @@ export * from './Toolbar'; import color from './utils/color'; export const effects = { color }; export { default as createStyledComponent } from './utils/createStyledComponent'; +export { Theme, ThemeFromProvider, Scheme } from './utils/theme'; diff --git a/packages/devui/src/utils/theme.ts b/packages/devui/src/utils/theme.ts index 5bfd788c..b0180cb7 100644 --- a/packages/devui/src/utils/theme.ts +++ b/packages/devui/src/utils/theme.ts @@ -3,19 +3,22 @@ import { nicinabox as defaultDarkScheme } from 'redux-devtools-themes'; import * as baseSchemes from 'base16'; import * as additionalSchemes from '../colorSchemes'; import invertColors from '../utils/invertColors'; -import { Theme } from '../themes/default'; +import { Theme as ThemeBase } from '../themes/default'; export const schemes = { ...baseSchemes, ...additionalSchemes }; export const listSchemes = () => Object.keys(schemes).slice(1).sort(); // remove `__esModule` export const listThemes = () => Object.keys(themes); +export type Theme = keyof typeof themes; +export type Scheme = keyof typeof schemes; + export interface ThemeData { theme: keyof typeof themes; scheme: keyof typeof schemes; light: boolean; } -export interface ThemeFromProvider extends Theme { +export interface ThemeFromProvider extends ThemeBase { type: keyof typeof themes; light: boolean; } diff --git a/packages/redux-devtools-chart-monitor/src/Chart.tsx b/packages/redux-devtools-chart-monitor/src/Chart.tsx index 8b646f58..f0b6d098 100644 --- a/packages/redux-devtools-chart-monitor/src/Chart.tsx +++ b/packages/redux-devtools-chart-monitor/src/Chart.tsx @@ -7,6 +7,7 @@ import * as themes from 'redux-devtools-themes'; import { Base16Theme } from 'react-base16-styling'; import { ChartMonitorState } from './reducers'; import { Primitive } from 'd3'; +import { NodeWithId } from 'd3-state-visualizer/lib/charts/tree/tree'; const wrapperStyle = { width: '100%', @@ -25,6 +26,7 @@ export interface Props> isSorted: boolean; heightBetweenNodesCoeff: number; widthBetweenNodesCoeff: number; + onClickText: (datum: NodeWithId) => void; tooltipOptions: { disabled: boolean; offset: { diff --git a/packages/redux-devtools-chart-monitor/src/ChartMonitor.tsx b/packages/redux-devtools-chart-monitor/src/ChartMonitor.tsx index 8fe44c64..df609820 100644 --- a/packages/redux-devtools-chart-monitor/src/ChartMonitor.tsx +++ b/packages/redux-devtools-chart-monitor/src/ChartMonitor.tsx @@ -9,6 +9,7 @@ import { Base16Theme } from 'react-base16-styling'; import reducer, { ChartMonitorState } from './reducers'; import Chart, { Props } from './Chart'; import { Primitive } from 'd3'; +import { NodeWithId } from 'd3-state-visualizer/lib/charts/tree/tree'; // eslint-disable-next-line @typescript-eslint/unbound-method const { reset, rollback, commit, sweep, toggleAction } = ActionCreators; @@ -49,6 +50,7 @@ export interface ChartMonitorProps> isSorted: boolean; heightBetweenNodesCoeff: number; widthBetweenNodesCoeff: number; + onClickText: (datum: NodeWithId) => void; tooltipOptions: unknown; style: { width: number; diff --git a/packages/redux-devtools-core/.babelrc b/packages/redux-devtools-core/.babelrc index e60d3036..0d42ef44 100644 --- a/packages/redux-devtools-core/.babelrc +++ b/packages/redux-devtools-core/.babelrc @@ -1,4 +1,8 @@ { - "presets": ["@babel/preset-env", "@babel/preset-react"], + "presets": [ + "@babel/preset-env", + "@babel/preset-react", + "@babel/preset-typescript" + ], "plugins": ["@babel/plugin-proposal-class-properties"] } diff --git a/packages/redux-devtools-core/.eslintignore b/packages/redux-devtools-core/.eslintignore new file mode 100644 index 00000000..79681bfb --- /dev/null +++ b/packages/redux-devtools-core/.eslintignore @@ -0,0 +1,2 @@ +lib +umd diff --git a/packages/redux-devtools-core/.eslintrc.js b/packages/redux-devtools-core/.eslintrc.js new file mode 100644 index 00000000..ce28cd35 --- /dev/null +++ b/packages/redux-devtools-core/.eslintrc.js @@ -0,0 +1,29 @@ +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'], + }, + }, + { + files: ['webpack.config.ts', 'webpack.config.umd.ts'], + extends: '../../eslintrc.ts.base.json', + parserOptions: { + tsconfigRootDir: __dirname, + project: ['./tsconfig.webpack.json'], + }, + }, + ], +}; diff --git a/packages/redux-devtools-core/jest.config.js b/packages/redux-devtools-core/jest.config.js new file mode 100644 index 00000000..547c49dd --- /dev/null +++ b/packages/redux-devtools-core/jest.config.js @@ -0,0 +1,4 @@ +module.exports = { + preset: 'ts-jest', + setupFilesAfterEnv: ['/test/setup.ts'], +}; diff --git a/packages/redux-devtools-core/package.json b/packages/redux-devtools-core/package.json index 8a9e2fb3..6b3a139f 100644 --- a/packages/redux-devtools-core/package.json +++ b/packages/redux-devtools-core/package.json @@ -2,65 +2,39 @@ "name": "redux-devtools-core", "version": "1.0.0-4", "description": "Reusable functions of Redux DevTools", - "scripts": { - "start": "webpack-dev-server --hot --inline --env.development --env.platform=web --progress", - "build:web": "rimraf ./build/web && webpack -p --env.platform=web --progress", - "build:umd": "rimraf ./umd && webpack --progress --config webpack.config.umd.js", - "build:umd:min": "webpack --env.production --progress --config webpack.config.umd.js", - "build": "rimraf ./lib && babel ./src/app --out-dir lib", - "clean": "rimraf lib", - "test": "jest --no-cache", - "prepare": "npm run build && npm run build:umd && npm run build:umd:min", - "prepublishOnly": "npm run test && npm run build && npm run build:umd && npm run build:umd:min" + "homepage": "https://github.com/reduxjs/redux-devtools/tree/master/packages/redux-devtools-core", + "bugs": { + "url": "https://github.com/reduxjs/redux-devtools/issues" }, - "main": "lib/index.js", + "license": "MIT", + "author": "Mihail Diordiev (https://github.com/zalmoxisus)", "files": [ "src", "lib", "umd" ], - "jest": { - "setupFilesAfterEnv": [ - "/test/setup.js" - ], - "moduleNameMapper": { - "\\.(css|scss)$": "/test/__mocks__/styleMock.js" - } - }, + "main": "lib/index.js", + "types": "lib/index.d.ts", "repository": { "type": "git", "url": "https://github.com/reduxjs/redux-devtools.git" }, - "author": "Mihail Diordiev (https://github.com/zalmoxisus)", - "license": "MIT", - "bugs": { - "url": "https://github.com/reduxjs/redux-devtools/issues" - }, - "homepage": "https://github.com/reduxjs/redux-devtools", - "devDependencies": { - "@babel/cli": "^7.10.5", - "@babel/core": "^7.11.1", - "@babel/plugin-proposal-class-properties": "^7.10.4", - "@babel/preset-env": "^7.11.0", - "@babel/preset-react": "^7.10.4", - "babel-loader": "^8.1.0", - "css-loader": "^4.2.1", - "enzyme": "^3.11.0", - "enzyme-adapter-react-16": "^1.15.3", - "enzyme-to-json": "^3.5.0", - "file-loader": "^6.0.0", - "html-loader": "^1.1.0", - "html-webpack-plugin": "^4.3.0", - "jest": "^26.2.2", - "react": "^16.13.1", - "react-dom": "^16.13.1", - "rimraf": "^3.0.2", - "style-loader": "^1.2.1", - "url-loader": "^4.1.0", - "webpack": "^4.44.1", - "webpack-cli": "^3.3.12", - "webpack-dev-server": "^3.11.0", - "webpack-hot-middleware": "^2.25.0" + "scripts": { + "start": "webpack-dev-server --hot --inline --env.development --env.platform=web --progress", + "build": "npm run build:types && npm run build:js && npm run build:web && npm run build:umd && npm run build:umd:min", + "build:types": "tsc --emitDeclarationOnly", + "build:js": "babel src --out-dir lib --extensions \".ts,.tsx\" --source-maps inline", + "build:web": "rimraf ./build/web && webpack -p --env.platform=web --progress", + "build:umd": "rimraf ./umd && webpack --progress --config webpack.config.umd.ts", + "build:umd:min": "webpack --env.production --progress --config webpack.config.umd.ts", + "clean": "rimraf lib", + "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": { "d3-state-visualizer": "^1.3.4", @@ -90,6 +64,34 @@ "socketcluster-client": "^14.3.1", "styled-components": "^5.1.1" }, + "devDependencies": { + "@babel/cli": "^7.10.5", + "@babel/core": "^7.11.1", + "@babel/plugin-proposal-class-properties": "^7.10.4", + "@babel/preset-env": "^7.11.0", + "@babel/preset-react": "^7.10.4", + "@rjsf/core": "^2.4.0", + "@types/json-schema": "^7.0.6", + "@types/socketcluster-client": "^13.0.3", + "babel-loader": "^8.1.0", + "css-loader": "^4.2.1", + "enzyme": "^3.11.0", + "enzyme-adapter-react-16": "^1.15.3", + "enzyme-to-json": "^3.5.0", + "file-loader": "^6.0.0", + "html-loader": "^1.1.0", + "html-webpack-plugin": "^4.3.0", + "jest": "^26.2.2", + "react": "^16.13.1", + "react-dom": "^16.13.1", + "rimraf": "^3.0.2", + "style-loader": "^1.2.1", + "url-loader": "^4.1.0", + "webpack": "^4.44.1", + "webpack-cli": "^3.3.12", + "webpack-dev-server": "^3.11.0", + "webpack-hot-middleware": "^2.25.0" + }, "peerDependencies": { "react": "^16.3.0" } diff --git a/packages/redux-devtools-core/src/app/actions/index.js b/packages/redux-devtools-core/src/app/actions/index.js deleted file mode 100644 index 9e568444..00000000 --- a/packages/redux-devtools-core/src/app/actions/index.js +++ /dev/null @@ -1,122 +0,0 @@ -import { - CHANGE_SECTION, - CHANGE_THEME, - SELECT_INSTANCE, - SELECT_MONITOR, - UPDATE_MONITOR_STATE, - LIFTED_ACTION, - MONITOR_ACTION, - EXPORT, - TOGGLE_SYNC, - TOGGLE_SLIDER, - TOGGLE_DISPATCHER, - TOGGLE_PERSIST, - GET_REPORT_REQUEST, - SHOW_NOTIFICATION, - CLEAR_NOTIFICATION, -} from '../constants/actionTypes'; -import { RECONNECT } from '../constants/socketActionTypes'; - -let monitorReducer; -let monitorProps = {}; - -export function changeSection(section) { - return { type: CHANGE_SECTION, section }; -} - -export function changeTheme(data) { - return { type: CHANGE_THEME, ...data.formData }; -} - -export function liftedDispatch(action) { - if (action.type[0] === '@') { - if (action.type === '@@INIT_MONITOR') { - monitorReducer = action.update; - monitorProps = action.monitorProps; - } - return { type: MONITOR_ACTION, action, monitorReducer, monitorProps }; - } - return { type: LIFTED_ACTION, message: 'DISPATCH', action }; -} - -export function selectInstance(selected) { - return { type: SELECT_INSTANCE, selected }; -} - -export function selectMonitor(monitor) { - return { type: SELECT_MONITOR, monitor }; -} - -export function selectMonitorWithState(value, monitorState) { - return { type: SELECT_MONITOR, monitor: value, monitorState }; -} - -export function selectMonitorTab(subTabName) { - return { type: UPDATE_MONITOR_STATE, nextState: { subTabName } }; -} - -export function updateMonitorState(nextState) { - return { type: UPDATE_MONITOR_STATE, nextState }; -} - -export function importState(state, preloadedState) { - return { type: LIFTED_ACTION, message: 'IMPORT', state, preloadedState }; -} - -export function exportState() { - return { type: EXPORT }; -} - -export function lockChanges(status) { - return { - type: LIFTED_ACTION, - message: 'DISPATCH', - action: { type: 'LOCK_CHANGES', status }, - toAll: true, - }; -} - -export function pauseRecording(status) { - return { - type: LIFTED_ACTION, - message: 'DISPATCH', - action: { type: 'PAUSE_RECORDING', status }, - toAll: true, - }; -} - -export function dispatchRemotely(action) { - return { type: LIFTED_ACTION, message: 'ACTION', action }; -} - -export function togglePersist() { - return { type: TOGGLE_PERSIST }; -} - -export function toggleSync() { - return { type: TOGGLE_SYNC }; -} - -export function toggleSlider() { - return { type: TOGGLE_SLIDER }; -} - -export function toggleDispatcher() { - return { type: TOGGLE_DISPATCHER }; -} - -export function saveSocketSettings(options) { - return { type: RECONNECT, options }; -} - -export function showNotification(message) { - return { type: SHOW_NOTIFICATION, notification: { type: 'error', message } }; -} - -export function clearNotification() { - return { type: CLEAR_NOTIFICATION }; -} - -export function getReport(report) { - return { type: GET_REPORT_REQUEST, report }; -} diff --git a/packages/redux-devtools-core/src/app/actions/index.ts b/packages/redux-devtools-core/src/app/actions/index.ts new file mode 100644 index 00000000..10617a12 --- /dev/null +++ b/packages/redux-devtools-core/src/app/actions/index.ts @@ -0,0 +1,573 @@ +import { Scheme, Theme } from 'devui'; +import { AuthStates, States } from 'socketcluster-client/lib/scclientsocket'; +import { + CHANGE_SECTION, + CHANGE_THEME, + SELECT_INSTANCE, + SELECT_MONITOR, + UPDATE_MONITOR_STATE, + LIFTED_ACTION, + MONITOR_ACTION, + EXPORT, + TOGGLE_SYNC, + TOGGLE_SLIDER, + TOGGLE_DISPATCHER, + TOGGLE_PERSIST, + GET_REPORT_REQUEST, + SHOW_NOTIFICATION, + CLEAR_NOTIFICATION, + UPDATE_STATE, + UPDATE_REPORTS, + REMOVE_INSTANCE, + SET_STATE, + GET_REPORT_ERROR, + GET_REPORT_SUCCESS, + ERROR, +} from '../constants/actionTypes'; +import { + AUTH_ERROR, + AUTH_REQUEST, + AUTH_SUCCESS, + CONNECT_ERROR, + CONNECT_REQUEST, + CONNECT_SUCCESS, + DEAUTHENTICATE, + DISCONNECTED, + EMIT, + RECONNECT, + SUBSCRIBE_ERROR, + SUBSCRIBE_REQUEST, + SUBSCRIBE_SUCCESS, + UNSUBSCRIBE, +} from '../constants/socketActionTypes'; +import { Action } from 'redux'; +import { Features, State } from '../reducers/instances'; +import { MonitorStateMonitorState } from '../reducers/monitor'; +import { LiftedAction } from 'redux-devtools-instrument'; +import { Data } from '../reducers/reports'; + +let monitorReducer: ( + monitorProps: unknown, + state: unknown | undefined, + action: Action +) => unknown; +let monitorProps: unknown = {}; + +interface ChangeSectionAction { + readonly type: typeof CHANGE_SECTION; + readonly section: string; +} +export function changeSection(section: string): ChangeSectionAction { + return { type: CHANGE_SECTION, section }; +} + +interface ChangeThemeFormData { + readonly theme: Theme; + readonly scheme: Scheme; + readonly dark: boolean; +} +interface ChangeThemeData { + readonly formData: ChangeThemeFormData; +} +interface ChangeThemeAction { + readonly type: typeof CHANGE_THEME; + readonly theme: Theme; + readonly scheme: Scheme; + readonly dark: boolean; +} +export function changeTheme(data: ChangeThemeData): ChangeThemeAction { + return { type: CHANGE_THEME, ...data.formData }; +} + +export interface InitMonitorAction { + type: '@@INIT_MONITOR'; + newMonitorState: unknown; + update: ( + monitorProps: unknown, + state: unknown | undefined, + action: Action + ) => unknown; + monitorProps: unknown; +} +export interface MonitorActionAction { + type: typeof MONITOR_ACTION; + action: InitMonitorAction; + monitorReducer: ( + monitorProps: unknown, + state: unknown | undefined, + action: Action + ) => unknown; + monitorProps: unknown; +} +export interface JumpToStateAction { + type: 'JUMP_TO_STATE'; + index: number; + actionId: number; +} +export interface JumpToActionAction { + type: 'JUMP_TO_ACTION'; + index: number; + actionId: number; +} +export interface PauseRecordingAction { + type: 'PAUSE_RECORDING'; + status: boolean; +} +export interface LockChangesAction { + type: 'LOCK_CHANGES'; + status: boolean; +} +export interface ToggleActionAction { + type: 'TOGGLE_ACTION'; +} +export interface RollbackAction { + type: 'ROLLBACK'; +} +export interface SweepAction { + type: 'SWEEP'; +} +export type DispatchAction = + | JumpToStateAction + | JumpToActionAction + | PauseRecordingAction + | LockChangesAction + | ToggleActionAction + | RollbackAction + | SweepAction; +interface LiftedActionActionBase { + action?: DispatchAction | string | CustomAction; + state?: string; + toAll?: boolean; +} +export interface LiftedActionDispatchAction extends LiftedActionActionBase { + type: typeof LIFTED_ACTION; + message: 'DISPATCH'; + action: DispatchAction; + toAll?: boolean; +} +interface LiftedActionImportAction extends LiftedActionActionBase { + type: typeof LIFTED_ACTION; + message: 'IMPORT'; + state: string; + preloadedState: unknown | undefined; +} +interface LiftedActionActionAction extends LiftedActionActionBase { + type: typeof LIFTED_ACTION; + message: 'ACTION'; + action: string | CustomAction; +} +interface LiftedActionExportAction extends LiftedActionActionBase { + type: typeof LIFTED_ACTION; + message: 'EXPORT'; + toExport: boolean; +} +export type LiftedActionAction = + | LiftedActionDispatchAction + | LiftedActionImportAction + | LiftedActionActionAction + | LiftedActionExportAction; +export function liftedDispatch( + action: + | InitMonitorAction + | JumpToStateAction + | JumpToActionAction + | LiftedAction, unknown> +): MonitorActionAction | LiftedActionDispatchAction { + if (action.type[0] === '@') { + if (action.type === '@@INIT_MONITOR') { + monitorReducer = action.update; + monitorProps = action.monitorProps; + } + return { + type: MONITOR_ACTION, + action, + monitorReducer, + monitorProps, + } as MonitorActionAction; + } + return { + type: LIFTED_ACTION, + message: 'DISPATCH', + action, + } as LiftedActionDispatchAction; +} + +interface SelectInstanceAction { + type: typeof SELECT_INSTANCE; + selected: string; +} +export function selectInstance(selected: string): SelectInstanceAction { + return { type: SELECT_INSTANCE, selected }; +} + +interface SelectMonitorAction { + type: typeof SELECT_MONITOR; + monitor: string; + monitorState?: MonitorStateMonitorState; +} +export function selectMonitor(monitor: string): SelectMonitorAction { + return { type: SELECT_MONITOR, monitor }; +} +export function selectMonitorWithState( + value: string, + monitorState: MonitorStateMonitorState +): SelectMonitorAction { + return { type: SELECT_MONITOR, monitor: value, monitorState }; +} + +interface NextState { + subTabName: string; + inspectedStatePath?: string[]; +} +interface UpdateMonitorStateAction { + type: typeof UPDATE_MONITOR_STATE; + nextState: NextState; +} +export function selectMonitorTab(subTabName: string): UpdateMonitorStateAction { + return { type: UPDATE_MONITOR_STATE, nextState: { subTabName } }; +} + +export function updateMonitorState( + nextState: NextState +): UpdateMonitorStateAction { + return { type: UPDATE_MONITOR_STATE, nextState }; +} + +export function importState( + state: string, + preloadedState?: unknown +): LiftedActionImportAction { + return { type: LIFTED_ACTION, message: 'IMPORT', state, preloadedState }; +} + +interface ExportAction { + type: typeof EXPORT; +} +export function exportState(): ExportAction { + return { type: EXPORT }; +} + +export function lockChanges(status: boolean): LiftedActionDispatchAction { + return { + type: LIFTED_ACTION, + message: 'DISPATCH', + action: { type: 'LOCK_CHANGES', status }, + toAll: true, + }; +} + +export function pauseRecording(status: boolean): LiftedActionDispatchAction { + return { + type: LIFTED_ACTION, + message: 'DISPATCH', + action: { type: 'PAUSE_RECORDING', status }, + toAll: true, + }; +} + +export interface CustomAction { + name: string; + selected: number; + args: (string | undefined)[]; + rest: string; +} +export function dispatchRemotely( + action: string | CustomAction +): LiftedActionActionAction { + return { type: LIFTED_ACTION, message: 'ACTION', action }; +} + +interface TogglePersistAction { + type: typeof TOGGLE_PERSIST; +} +export function togglePersist(): TogglePersistAction { + return { type: TOGGLE_PERSIST }; +} + +interface ToggleSyncAction { + type: typeof TOGGLE_SYNC; +} +export function toggleSync(): ToggleSyncAction { + return { type: TOGGLE_SYNC }; +} + +interface ToggleSliderAction { + type: typeof TOGGLE_SLIDER; +} +export function toggleSlider(): ToggleSliderAction { + return { type: TOGGLE_SLIDER }; +} + +interface ToggleDispatcherAction { + type: typeof TOGGLE_DISPATCHER; +} +export function toggleDispatcher(): ToggleDispatcherAction { + return { type: TOGGLE_DISPATCHER }; +} + +export type ConnectionType = 'disabled' | 'remotedev' | 'custom'; +export interface ConnectionOptions { + readonly type: ConnectionType; + readonly hostname: string; + readonly port: number; + readonly secure: boolean; +} +interface ReconnectAction { + readonly type: typeof RECONNECT; + readonly options: ConnectionOptions; +} +export function saveSocketSettings( + options: ConnectionOptions +): ReconnectAction { + return { type: RECONNECT, options }; +} + +interface Notification { + readonly type: 'error'; + readonly message: string; +} +interface ShowNotificationAction { + readonly type: typeof SHOW_NOTIFICATION; + readonly notification: Notification; +} +export function showNotification(message: string): ShowNotificationAction { + return { type: SHOW_NOTIFICATION, notification: { type: 'error', message } }; +} + +interface ClearNotificationAction { + readonly type: typeof CLEAR_NOTIFICATION; +} +export function clearNotification(): ClearNotificationAction { + return { type: CLEAR_NOTIFICATION }; +} + +interface GetReportRequest { + readonly type: typeof GET_REPORT_REQUEST; + readonly report: unknown; +} +export function getReport(report: unknown): GetReportRequest { + return { type: GET_REPORT_REQUEST, report }; +} + +export interface ActionCreator { + args: string[]; + name: string; +} + +interface LibConfig { + actionCreators?: string; + name?: string; + type?: string; + features?: Features; + serialize?: boolean; +} + +export interface RequestBase { + id: string; + instanceId?: string; + action?: string; + name?: string; + libConfig?: LibConfig; + actionsById?: string; + computedStates?: string; + // eslint-disable-next-line @typescript-eslint/ban-types + payload?: {} | string; + liftedState?: Partial; +} +interface InitRequest extends RequestBase { + type: 'INIT'; + action: string; +} +interface ActionRequest extends RequestBase { + type: 'ACTION'; + isExcess: boolean; + nextActionId: number; + maxAge: number; + batched: boolean; +} +interface StateRequest extends RequestBase { + type: 'STATE'; + committedState: unknown; +} +interface PartialStateRequest extends RequestBase { + type: 'PARTIAL_STATE'; + committedState: unknown; + maxAge: number; +} +interface LiftedRequest extends RequestBase { + type: 'LIFTED'; +} +export interface ExportRequest extends RequestBase { + type: 'EXPORT'; + committedState: unknown; +} +export type Request = + | InitRequest + | ActionRequest + | StateRequest + | PartialStateRequest + | LiftedRequest + | ExportRequest; + +interface UpdateStateAction { + type: typeof UPDATE_STATE; + request?: Request; + id?: string; +} + +interface SetStateAction { + type: typeof SET_STATE; + newState: State; +} + +interface RemoveInstanceAction { + type: typeof REMOVE_INSTANCE; + id: string; +} + +interface ConnectRequestAction { + type: typeof CONNECT_REQUEST; + options: ConnectionOptions; +} + +interface ConnectSuccessPayload { + id: string; + authState: AuthStates; + socketState: States; +} +interface ConnectSuccessAction { + type: typeof CONNECT_SUCCESS; + payload: ConnectSuccessPayload; + error: Error | undefined; +} + +interface ConnectErrorAction { + type: typeof CONNECT_ERROR; + error: Error | undefined; +} + +interface AuthRequestAction { + type: typeof AUTH_REQUEST; +} + +interface AuthSuccessAction { + type: typeof AUTH_SUCCESS; + baseChannel: string; +} + +interface AuthErrorAction { + type: typeof AUTH_ERROR; + error: Error; +} + +interface DisconnectedAction { + type: typeof DISCONNECTED; + code: number; +} + +interface DeauthenticateAction { + type: typeof DEAUTHENTICATE; +} + +interface SubscribeRequestAction { + type: typeof SUBSCRIBE_REQUEST; + channel: string; + subscription: typeof UPDATE_STATE | typeof UPDATE_REPORTS; +} + +interface SubscribeSuccessAction { + type: typeof SUBSCRIBE_SUCCESS; + channel: string; +} + +interface SubscribeErrorAction { + type: typeof SUBSCRIBE_ERROR; + error: Error; + status: string; +} + +interface UnsubscribeAction { + type: typeof UNSUBSCRIBE; + channel: string; +} + +export interface EmitAction { + type: typeof EMIT; + message: string; + id?: string | false; + instanceId?: string; + action?: unknown; + state?: unknown; +} + +interface ListRequest { + type: 'list'; + data: Data[]; +} +interface AddRequest { + type: 'add'; + data: Data; +} +interface RemoveRequest { + type: 'remove'; + data: Data; + id: unknown; +} +export type UpdateReportsRequest = ListRequest | AddRequest | RemoveRequest; +interface UpdateReportsAction { + type: typeof UPDATE_REPORTS; + request: UpdateReportsRequest; +} + +interface GetReportError { + type: typeof GET_REPORT_ERROR; + error: Error; +} + +interface GetReportSuccess { + type: typeof GET_REPORT_SUCCESS; + data: { payload: string }; +} + +interface ErrorAction { + type: typeof ERROR; + payload: string; +} + +export type StoreAction = + | ChangeSectionAction + | ChangeThemeAction + | MonitorActionAction + | LiftedActionAction + | SelectInstanceAction + | SelectMonitorAction + | UpdateMonitorStateAction + | ExportAction + | TogglePersistAction + | ToggleSyncAction + | ToggleSliderAction + | ToggleDispatcherAction + | ReconnectAction + | ShowNotificationAction + | ClearNotificationAction + | GetReportRequest + | SetStateAction + | UpdateStateAction + | RemoveInstanceAction + | ConnectRequestAction + | ConnectSuccessAction + | ConnectErrorAction + | AuthRequestAction + | AuthSuccessAction + | AuthErrorAction + | DisconnectedAction + | DeauthenticateAction + | SubscribeRequestAction + | SubscribeSuccessAction + | SubscribeErrorAction + | UnsubscribeAction + | EmitAction + | UpdateReportsAction + | GetReportError + | GetReportSuccess + | ErrorAction; diff --git a/packages/redux-devtools-core/src/app/components/BottomButtons.js b/packages/redux-devtools-core/src/app/components/BottomButtons.tsx similarity index 85% rename from packages/redux-devtools-core/src/app/components/BottomButtons.js rename to packages/redux-devtools-core/src/app/components/BottomButtons.tsx index d7e0f33b..da8788a9 100644 --- a/packages/redux-devtools-core/src/app/components/BottomButtons.js +++ b/packages/redux-devtools-core/src/app/components/BottomButtons.tsx @@ -8,15 +8,22 @@ import PrintButton from './buttons/PrintButton'; import DispatcherButton from './buttons/DispatcherButton'; import SliderButton from './buttons/SliderButton'; import MonitorSelector from './MonitorSelector'; +import { Options } from '../reducers/instances'; -export default class BottomButtons extends Component { +interface Props { + dispatcherIsOpen: boolean; + sliderIsOpen: boolean; + options: Options; +} + +export default class BottomButtons extends Component { static propTypes = { dispatcherIsOpen: PropTypes.bool, sliderIsOpen: PropTypes.bool, options: PropTypes.object.isRequired, }; - shouldComponentUpdate(nextProps) { + shouldComponentUpdate(nextProps: Props) { return ( nextProps.dispatcherIsOpen !== this.props.dispatcherIsOpen || nextProps.sliderIsOpen !== this.props.sliderIsOpen || diff --git a/packages/redux-devtools-core/src/app/components/Header.js b/packages/redux-devtools-core/src/app/components/Header.tsx similarity index 77% rename from packages/redux-devtools-core/src/app/components/Header.js rename to packages/redux-devtools-core/src/app/components/Header.tsx index ff80d2b8..c9a42bd2 100644 --- a/packages/redux-devtools-core/src/app/components/Header.js +++ b/packages/redux-devtools-core/src/app/components/Header.tsx @@ -1,8 +1,6 @@ import React, { Component } from 'react'; -import PropTypes from 'prop-types'; import { Tabs, Toolbar, Button, Divider } from 'devui'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; +import { connect, ResolveThunks } from 'react-redux'; import { GoBook } from 'react-icons/go'; import { IoMdText } from 'react-icons/io'; import { TiSocialTwitter } from 'react-icons/ti'; @@ -11,13 +9,14 @@ import { changeSection } from '../actions'; const tabs = [{ name: 'Actions' }, { name: 'Reports' }, { name: 'Settings' }]; -class Header extends Component { - static propTypes = { - section: PropTypes.string.isRequired, - changeSection: PropTypes.func.isRequired, - }; +type DispatchProps = ResolveThunks; +interface OwnProps { + readonly section: string; +} +type Props = DispatchProps & OwnProps; - openLink = (url) => () => { +class Header extends Component { + openLink = (url: string) => () => { window.open(url); }; @@ -69,10 +68,8 @@ class Header extends Component { } } -function mapDispatchToProps(dispatch) { - return { - changeSection: bindActionCreators(changeSection, dispatch), - }; -} +const actionCreators = { + changeSection, +}; -export default connect(null, mapDispatchToProps)(Header); +export default connect(null, actionCreators)(Header); diff --git a/packages/redux-devtools-core/src/app/components/InstanceSelector.js b/packages/redux-devtools-core/src/app/components/InstanceSelector.js deleted file mode 100644 index 3cd30bac..00000000 --- a/packages/redux-devtools-core/src/app/components/InstanceSelector.js +++ /dev/null @@ -1,48 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; -import { Select } from 'devui'; -import { selectInstance } from '../actions'; - -class InstanceSelector extends Component { - static propTypes = { - selected: PropTypes.string, - instances: PropTypes.object.isRequired, - onSelect: PropTypes.func.isRequired, - }; - - render() { - this.select = [{ value: '', label: 'Autoselect instances' }]; - const instances = this.props.instances; - let name; - Object.keys(instances).forEach((key) => { - name = instances[key].name; - if (name !== undefined) - this.select.push({ value: key, label: instances[key].name }); - }); - - return ( - + ); + } +} + +const mapStateToProps = (state: StoreState) => ({ + selected: state.instances.selected, + instances: state.instances.options, +}); + +const actionCreators = { + onSelect: selectInstance, +}; + +export default connect(mapStateToProps, actionCreators)(InstanceSelector); diff --git a/packages/redux-devtools-core/src/app/components/MonitorSelector.js b/packages/redux-devtools-core/src/app/components/MonitorSelector.js deleted file mode 100644 index f0167385..00000000 --- a/packages/redux-devtools-core/src/app/components/MonitorSelector.js +++ /dev/null @@ -1,45 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; -import { Tabs } from 'devui'; -import { monitors } from '../utils/getMonitor'; -import { selectMonitor } from '../actions'; - -class MonitorSelector extends Component { - static propTypes = { - selected: PropTypes.string, - selectMonitor: PropTypes.func.isRequired, - }; - - shouldComponentUpdate(nextProps) { - return nextProps.selected !== this.props.selected; - } - - render() { - return ( - - ); - } -} - -function mapStateToProps(state) { - return { - selected: state.monitor.selected, - }; -} - -function mapDispatchToProps(dispatch) { - return { - selectMonitor: bindActionCreators(selectMonitor, dispatch), - }; -} - -export default connect(mapStateToProps, mapDispatchToProps)(MonitorSelector); diff --git a/packages/redux-devtools-core/src/app/components/MonitorSelector.tsx b/packages/redux-devtools-core/src/app/components/MonitorSelector.tsx new file mode 100644 index 00000000..e606008b --- /dev/null +++ b/packages/redux-devtools-core/src/app/components/MonitorSelector.tsx @@ -0,0 +1,39 @@ +import React, { Component } from 'react'; +import { connect, ResolveThunks } from 'react-redux'; +import { Tabs } from 'devui'; +import { monitors } from '../utils/getMonitor'; +import { selectMonitor } from '../actions'; +import { StoreState } from '../reducers'; + +type StateProps = ReturnType; +type DispatchProps = ResolveThunks; +type Props = StateProps & DispatchProps; + +class MonitorSelector extends Component { + shouldComponentUpdate(nextProps: Props) { + return nextProps.selected !== this.props.selected; + } + + render() { + return ( + + ); + } +} + +const mapStateToProps = (state: StoreState) => ({ + selected: state.monitor.selected, +}); + +const actionCreators = { + selectMonitor, +}; + +export default connect(mapStateToProps, actionCreators)(MonitorSelector); diff --git a/packages/redux-devtools-core/src/app/components/Settings/Connection.js b/packages/redux-devtools-core/src/app/components/Settings/Connection.tsx similarity index 55% rename from packages/redux-devtools-core/src/app/components/Settings/Connection.js rename to packages/redux-devtools-core/src/app/components/Settings/Connection.tsx index cb46281e..9b1e1ac9 100644 --- a/packages/redux-devtools-core/src/app/components/Settings/Connection.js +++ b/packages/redux-devtools-core/src/app/components/Settings/Connection.tsx @@ -1,11 +1,32 @@ import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; +import { connect, ResolveThunks } from 'react-redux'; import { Container, Form } from 'devui'; -import { saveSocketSettings } from '../../actions'; +import { + JSONSchema7, + JSONSchema7Definition, + JSONSchema7Type, + JSONSchema7TypeName, +} from 'json-schema'; +import { ConnectionType, saveSocketSettings } from '../../actions'; +import { StoreState } from '../../reducers'; +import { ConnectionOptions } from '../../reducers/connection'; +import { IChangeEvent, ISubmitEvent } from '@rjsf/core'; -const defaultSchema = { +declare module 'json-schema' { + export interface JSONSchema7 { + enumNames?: JSONSchema7Type[]; + } +} + +interface Schema { + type: JSONSchema7TypeName; + required?: string[]; + properties: { + [key: string]: JSONSchema7Definition; + }; +} + +const defaultSchema: Schema = { type: 'object', required: [], properties: { @@ -37,37 +58,24 @@ const uiSchema = { }, }; -class Connection extends Component { - static propTypes = { - saveSettings: PropTypes.func.isRequired, - options: PropTypes.object.isRequired, - type: PropTypes.string, - }; +type StateProps = ReturnType; +type DispatchProps = ResolveThunks; +type Props = StateProps & DispatchProps; - constructor(props) { - super(props); - this.state = this.setFormData(props.type); - } +interface FormData extends ConnectionOptions { + readonly type: ConnectionType; +} - shouldComponentUpdate(nextProps, nextState) { - return this.state !== nextState; - } +interface State { + readonly formData: FormData; + readonly type: ConnectionType; + readonly schema: Schema; + readonly changed: boolean | undefined; +} - UNSAFE_componentWillReceiveProps(nextProps) { - if (this.props.options !== nextProps.options) { - this.setState({ - formData: { ...nextProps.options, type: nextProps.type }, - }); - } - } - - handleSave = (data) => { - this.props.saveSettings(data.formData); - this.setState({ changed: false }); - }; - - setFormData = (type, changed) => { - let schema; +export class Connection extends Component { + setFormData = (type: ConnectionType, changed?: boolean) => { + let schema: Schema; if (type !== 'custom') { schema = { type: 'object', @@ -87,7 +95,26 @@ class Connection extends Component { }; }; - handleChange = (data) => { + state: State = this.setFormData(this.props.type); + + shouldComponentUpdate(nextProps: Props, nextState: State) { + return this.state !== nextState; + } + + UNSAFE_componentWillReceiveProps(nextProps: Props) { + if (this.props.options !== nextProps.options) { + this.setState({ + formData: { ...nextProps.options, type: nextProps.type }, + }); + } + } + + handleSave = (data: ISubmitEvent) => { + this.props.saveSettings(data.formData); + this.setState({ changed: false }); + }; + + handleChange = (data: IChangeEvent) => { const formData = data.formData; const type = formData.type; if (type !== this.state.type) { @@ -119,14 +146,10 @@ class Connection extends Component { } } -function mapStateToProps(state) { - return state.connection; -} +const mapStateToProps = (state: StoreState) => state.connection; -function mapDispatchToProps(dispatch) { - return { - saveSettings: bindActionCreators(saveSocketSettings, dispatch), - }; -} +const actionCreators = { + saveSettings: saveSocketSettings, +}; -export default connect(mapStateToProps, mapDispatchToProps)(Connection); +export default connect(mapStateToProps, actionCreators)(Connection); diff --git a/packages/redux-devtools-core/src/app/components/Settings/Themes.js b/packages/redux-devtools-core/src/app/components/Settings/Themes.tsx similarity index 64% rename from packages/redux-devtools-core/src/app/components/Settings/Themes.js rename to packages/redux-devtools-core/src/app/components/Settings/Themes.tsx index e528dd55..7762454e 100644 --- a/packages/redux-devtools-core/src/app/components/Settings/Themes.js +++ b/packages/redux-devtools-core/src/app/components/Settings/Themes.tsx @@ -1,17 +1,15 @@ import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; +import { connect, ResolveThunks } from 'react-redux'; import { Container, Form } from 'devui'; import { listSchemes, listThemes } from 'devui/lib/utils/theme'; import { changeTheme } from '../../actions'; +import { StoreState } from '../../reducers'; -class Themes extends Component { - static propTypes = { - changeTheme: PropTypes.func.isRequired, - theme: PropTypes.object.isRequired, - }; +type StateProps = ReturnType; +type DispatchProps = ResolveThunks; +type Props = StateProps & DispatchProps; +export class Themes extends Component { render() { const theme = this.props.theme; const formData = { @@ -49,16 +47,12 @@ class Themes extends Component { } } -function mapStateToProps(state) { - return { - theme: state.theme, - }; -} +const mapStateToProps = (state: StoreState) => ({ + theme: state.theme, +}); -function mapDispatchToProps(dispatch) { - return { - changeTheme: bindActionCreators(changeTheme, dispatch), - }; -} +const actionCreators = { + changeTheme, +}; -export default connect(mapStateToProps, mapDispatchToProps)(Themes); +export default connect(mapStateToProps, actionCreators)(Themes); diff --git a/packages/redux-devtools-core/src/app/components/Settings/index.js b/packages/redux-devtools-core/src/app/components/Settings/index.js deleted file mode 100644 index 47b62874..00000000 --- a/packages/redux-devtools-core/src/app/components/Settings/index.js +++ /dev/null @@ -1,32 +0,0 @@ -import React, { Component } from 'react'; -import { Tabs } from 'devui'; -import Connection from './Connection'; -import Themes from './Themes'; - -class Settings extends Component { - constructor(props) { - super(props); - this.tabs = [ - { name: 'Connection', component: Connection }, - { name: 'Themes', component: Themes }, - ]; - this.state = { selected: 'Connection' }; - } - - handleSelect = (selected) => { - this.setState({ selected }); - }; - - render() { - return ( - - ); - } -} - -export default Settings; diff --git a/packages/redux-devtools-core/src/app/components/Settings/index.tsx b/packages/redux-devtools-core/src/app/components/Settings/index.tsx new file mode 100644 index 00000000..ae18464a --- /dev/null +++ b/packages/redux-devtools-core/src/app/components/Settings/index.tsx @@ -0,0 +1,34 @@ +import React, { Component } from 'react'; +import { Tabs } from 'devui'; +import Connection from './Connection'; +import Themes from './Themes'; + +interface State { + selected: string; +} + +// eslint-disable-next-line @typescript-eslint/ban-types +class Settings extends Component<{}, State> { + tabs = [ + { name: 'Connection', component: Connection }, + { name: 'Themes', component: Themes }, + ]; + state: State = { selected: 'Connection' }; + + handleSelect = (selected: string) => { + this.setState({ selected }); + }; + + render() { + return ( + // eslint-disable-next-line @typescript-eslint/ban-types + + tabs={this.tabs as any} + selected={this.state.selected} + onClick={this.handleSelect} + /> + ); + } +} + +export default Settings; diff --git a/packages/redux-devtools-core/src/app/components/TopButtons.js b/packages/redux-devtools-core/src/app/components/TopButtons.tsx similarity index 84% rename from packages/redux-devtools-core/src/app/components/TopButtons.js rename to packages/redux-devtools-core/src/app/components/TopButtons.tsx index 7c2f3fc8..e183742a 100644 --- a/packages/redux-devtools-core/src/app/components/TopButtons.js +++ b/packages/redux-devtools-core/src/app/components/TopButtons.tsx @@ -1,16 +1,25 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { ActionCreators } from 'redux-devtools-instrument'; +import { ActionCreators, LiftedAction } from 'redux-devtools-instrument'; import { Button, Toolbar, Divider } from 'devui'; +import { Action } from 'redux'; import RecordButton from './buttons/RecordButton'; import PersistButton from './buttons/PersistButton'; import LockButton from './buttons/LockButton'; import InstanceSelector from './InstanceSelector'; import SyncButton from './buttons/SyncButton'; +import { Options, State } from '../reducers/instances'; +// eslint-disable-next-line @typescript-eslint/unbound-method const { reset, rollback, commit, sweep } = ActionCreators; -export default class TopButtons extends Component { +interface Props { + dispatch: (action: LiftedAction, unknown>) => void; + liftedState: State; + options: Options; +} + +export default class TopButtons extends Component { static propTypes = { // shouldSync: PropTypes.bool, liftedState: PropTypes.object.isRequired, @@ -18,7 +27,7 @@ export default class TopButtons extends Component { options: PropTypes.object.isRequired, }; - shouldComponentUpdate(nextProps) { + shouldComponentUpdate(nextProps: Props) { return ( nextProps.options !== this.props.options || nextProps.liftedState !== this.props.liftedState diff --git a/packages/redux-devtools-core/src/app/components/buttons/DispatcherButton.js b/packages/redux-devtools-core/src/app/components/buttons/DispatcherButton.tsx similarity index 53% rename from packages/redux-devtools-core/src/app/components/buttons/DispatcherButton.js rename to packages/redux-devtools-core/src/app/components/buttons/DispatcherButton.tsx index 77f054ae..f148f0c6 100644 --- a/packages/redux-devtools-core/src/app/components/buttons/DispatcherButton.js +++ b/packages/redux-devtools-core/src/app/components/buttons/DispatcherButton.tsx @@ -1,18 +1,17 @@ import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; +import { connect, ResolveThunks } from 'react-redux'; import { Button } from 'devui'; import { FaTerminal } from 'react-icons/fa'; import { toggleDispatcher } from '../../actions'; -class DispatcherButton extends Component { - static propTypes = { - dispatcherIsOpen: PropTypes.bool, - toggleDispatcher: PropTypes.func.isRequired, - }; +type DispatchProps = ResolveThunks; +interface OwnProps { + dispatcherIsOpen: boolean; +} +type Props = DispatchProps & OwnProps; - shouldComponentUpdate(nextProps) { +class DispatcherButton extends Component { + shouldComponentUpdate(nextProps: Props) { return nextProps.dispatcherIsOpen !== this.props.dispatcherIsOpen; } @@ -32,10 +31,8 @@ class DispatcherButton extends Component { } } -function mapDispatchToProps(dispatch) { - return { - toggleDispatcher: bindActionCreators(toggleDispatcher, dispatch), - }; -} +const actionCreators = { + toggleDispatcher, +}; -export default connect(null, mapDispatchToProps)(DispatcherButton); +export default connect(null, actionCreators)(DispatcherButton); diff --git a/packages/redux-devtools-core/src/app/components/buttons/ExportButton.js b/packages/redux-devtools-core/src/app/components/buttons/ExportButton.js deleted file mode 100644 index 92659c95..00000000 --- a/packages/redux-devtools-core/src/app/components/buttons/ExportButton.js +++ /dev/null @@ -1,33 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; -import { Button } from 'devui'; -import { TiDownload } from 'react-icons/ti'; -import { exportState } from '../../actions'; - -class ExportButton extends Component { - static propTypes = { - exportState: PropTypes.func.isRequired, - }; - - shouldComponentUpdate() { - return false; - } - - render() { - return ( - - ); - } -} - -function mapDispatchToProps(dispatch) { - return { - exportState: bindActionCreators(exportState, dispatch), - }; -} - -export default connect(null, mapDispatchToProps)(ExportButton); diff --git a/packages/redux-devtools-core/src/app/components/buttons/ExportButton.tsx b/packages/redux-devtools-core/src/app/components/buttons/ExportButton.tsx new file mode 100644 index 00000000..0276d94a --- /dev/null +++ b/packages/redux-devtools-core/src/app/components/buttons/ExportButton.tsx @@ -0,0 +1,28 @@ +import React, { Component } from 'react'; +import { connect, ResolveThunks } from 'react-redux'; +import { Button } from 'devui'; +import { TiDownload } from 'react-icons/ti'; +import { exportState } from '../../actions'; + +type DispatchProps = ResolveThunks; +type Props = DispatchProps; + +class ExportButton extends Component { + shouldComponentUpdate() { + return false; + } + + render() { + return ( + + ); + } +} + +const actionCreators = { + exportState, +}; + +export default connect(null, actionCreators)(ExportButton); diff --git a/packages/redux-devtools-core/src/app/components/buttons/ImportButton.js b/packages/redux-devtools-core/src/app/components/buttons/ImportButton.js deleted file mode 100644 index 89cd7d70..00000000 --- a/packages/redux-devtools-core/src/app/components/buttons/ImportButton.js +++ /dev/null @@ -1,64 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; -import { Button } from 'devui'; -import { TiUpload } from 'react-icons/ti'; -import { importState } from '../../actions'; - -class ImportButton extends Component { - static propTypes = { - importState: PropTypes.func.isRequired, - }; - - constructor() { - super(); - this.handleImport = this.handleImport.bind(this); - this.handleImportFile = this.handleImportFile.bind(this); - this.mapRef = this.mapRef.bind(this); - } - - shouldComponentUpdate() { - return false; - } - - mapRef(node) { - this.fileInput = node; - } - - handleImport() { - this.fileInput.click(); - } - - handleImportFile(e) { - const file = e.target.files[0]; - const reader = new FileReader(); - reader.onload = () => { - this.props.importState(reader.result); - }; - reader.readAsText(file); - e.target.value = ''; // eslint-disable-line no-param-reassign - } - - render() { - return ( - - ); - } -} - -function mapDispatchToProps(dispatch) { - return { - importState: bindActionCreators(importState, dispatch), - }; -} - -export default connect(null, mapDispatchToProps)(ImportButton); diff --git a/packages/redux-devtools-core/src/app/components/buttons/ImportButton.tsx b/packages/redux-devtools-core/src/app/components/buttons/ImportButton.tsx new file mode 100644 index 00000000..0b33a042 --- /dev/null +++ b/packages/redux-devtools-core/src/app/components/buttons/ImportButton.tsx @@ -0,0 +1,54 @@ +import React, { ChangeEventHandler, Component, RefCallback } from 'react'; +import { connect, ResolveThunks } from 'react-redux'; +import { Button } from 'devui'; +import { TiUpload } from 'react-icons/ti'; +import { importState } from '../../actions'; + +type DispatchProps = ResolveThunks; +type Props = DispatchProps; + +class ImportButton extends Component { + fileInput?: HTMLInputElement | null; + + shouldComponentUpdate() { + return false; + } + + mapRef: RefCallback = (node) => { + this.fileInput = node; + }; + + handleImport = () => { + this.fileInput!.click(); + }; + + handleImportFile: ChangeEventHandler = (e) => { + const file = e.target.files![0]; + const reader = new FileReader(); + reader.onload = () => { + this.props.importState(reader.result as string); + }; + reader.readAsText(file); + e.target.value = ''; + }; + + render() { + return ( + + ); + } +} + +const actionCreators = { + importState, +}; + +export default connect(null, actionCreators)(ImportButton); diff --git a/packages/redux-devtools-core/src/app/components/buttons/LockButton.js b/packages/redux-devtools-core/src/app/components/buttons/LockButton.tsx similarity index 60% rename from packages/redux-devtools-core/src/app/components/buttons/LockButton.js rename to packages/redux-devtools-core/src/app/components/buttons/LockButton.tsx index 0bd642f7..bf338f68 100644 --- a/packages/redux-devtools-core/src/app/components/buttons/LockButton.js +++ b/packages/redux-devtools-core/src/app/components/buttons/LockButton.tsx @@ -1,18 +1,19 @@ import React, { Component } from 'react'; -import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { Button } from 'devui'; import { IoIosLock } from 'react-icons/io'; -import { lockChanges } from '../../actions'; +import { lockChanges, StoreAction } from '../../actions'; +import { Dispatch } from 'redux'; -class LockButton extends Component { - static propTypes = { - locked: PropTypes.bool, - disabled: PropTypes.bool, - lockChanges: PropTypes.func.isRequired, - }; +type DispatchProps = ReturnType; +interface OwnProps { + locked: boolean | undefined; + disabled: boolean; +} +type Props = DispatchProps & OwnProps; - shouldComponentUpdate(nextProps) { +class LockButton extends Component { + shouldComponentUpdate(nextProps: Props) { return nextProps.locked !== this.props.locked; } @@ -31,7 +32,10 @@ class LockButton extends Component { } } -function mapDispatchToProps(dispatch, ownProps) { +function mapDispatchToProps( + dispatch: Dispatch, + ownProps: OwnProps +) { return { lockChanges: () => dispatch(lockChanges(!ownProps.locked)), }; diff --git a/packages/redux-devtools-core/src/app/components/buttons/PersistButton.js b/packages/redux-devtools-core/src/app/components/buttons/PersistButton.tsx similarity index 52% rename from packages/redux-devtools-core/src/app/components/buttons/PersistButton.js rename to packages/redux-devtools-core/src/app/components/buttons/PersistButton.tsx index da1c0590..e78349a5 100644 --- a/packages/redux-devtools-core/src/app/components/buttons/PersistButton.js +++ b/packages/redux-devtools-core/src/app/components/buttons/PersistButton.tsx @@ -1,26 +1,26 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; +import { connect, ResolveThunks } from 'react-redux'; import { Button } from 'devui'; import { FaThumbtack } from 'react-icons/fa'; import { togglePersist } from '../../actions'; +import { StoreState } from '../../reducers'; -class LockButton extends Component { - static propTypes = { - persisted: PropTypes.bool, - disabled: PropTypes.bool, - onClick: PropTypes.func.isRequired, - }; +type StateProps = ReturnType; +type DispatchProps = ResolveThunks; +interface OwnProps { + disabled?: boolean; +} +type Props = StateProps & DispatchProps & OwnProps; - shouldComponentUpdate(nextProps) { +class LockButton extends Component { + shouldComponentUpdate(nextProps: Props) { return nextProps.persisted !== this.props.persisted; } render() { return ( - ); - } -} - -function mapStateToProps(state) { - return { - sync: state.instances.sync, - }; -} - -function mapDispatchToProps(dispatch) { - return { - onClick: bindActionCreators(toggleSync, dispatch), - }; -} - -export default connect(mapStateToProps, mapDispatchToProps)(SyncButton); diff --git a/packages/redux-devtools-core/src/app/components/buttons/SyncButton.tsx b/packages/redux-devtools-core/src/app/components/buttons/SyncButton.tsx new file mode 100644 index 00000000..5c8a8e4d --- /dev/null +++ b/packages/redux-devtools-core/src/app/components/buttons/SyncButton.tsx @@ -0,0 +1,39 @@ +import React, { Component } from 'react'; +import { connect, ResolveThunks } from 'react-redux'; +import { Button } from 'devui'; +import { TiArrowSync } from 'react-icons/ti'; +import { toggleSync } from '../../actions'; +import { StoreState } from '../../reducers'; + +type StateProps = ReturnType; +type DispatchProps = ResolveThunks; +type Props = StateProps & DispatchProps; + +class SyncButton extends Component { + shouldComponentUpdate(nextProps: Props) { + return nextProps.sync !== this.props.sync; + } + + render() { + return ( + + ); + } +} + +const mapStateToProps = (state: StoreState) => ({ + sync: state.instances.sync, +}); + +const actionCreators = { + onClick: toggleSync, +}; + +export default connect(mapStateToProps, actionCreators)(SyncButton); diff --git a/packages/redux-devtools-core/src/app/constants/actionTypes.js b/packages/redux-devtools-core/src/app/constants/actionTypes.ts similarity index 100% rename from packages/redux-devtools-core/src/app/constants/actionTypes.js rename to packages/redux-devtools-core/src/app/constants/actionTypes.ts diff --git a/packages/redux-devtools-core/src/app/constants/dataTypes.js b/packages/redux-devtools-core/src/app/constants/dataTypes.ts similarity index 100% rename from packages/redux-devtools-core/src/app/constants/dataTypes.js rename to packages/redux-devtools-core/src/app/constants/dataTypes.ts diff --git a/packages/redux-devtools-core/src/app/constants/socketActionTypes.js b/packages/redux-devtools-core/src/app/constants/socketActionTypes.ts similarity index 78% rename from packages/redux-devtools-core/src/app/constants/socketActionTypes.js rename to packages/redux-devtools-core/src/app/constants/socketActionTypes.ts index 95e00b6a..825dbb77 100644 --- a/packages/redux-devtools-core/src/app/constants/socketActionTypes.js +++ b/packages/redux-devtools-core/src/app/constants/socketActionTypes.ts @@ -1,5 +1,14 @@ import socketCluster from 'socketcluster-client'; +interface States { + CLOSED: 'closed'; + CONNECTING: 'connecting'; + OPEN: 'open'; + AUTHENTICATED: 'authenticated'; + PENDING: 'pending'; + UNAUTHENTICATED: 'unauthenticated'; +} + export const { CLOSED, CONNECTING, @@ -7,7 +16,7 @@ export const { AUTHENTICATED, PENDING, UNAUTHENTICATED, -} = socketCluster.SCClientSocket; +} = (socketCluster.SCClientSocket as unknown) as States; export const CONNECT_REQUEST = 'socket/CONNECT_REQUEST'; export const CONNECT_SUCCESS = 'socket/CONNECT_SUCCESS'; export const CONNECT_ERROR = 'socket/CONNECT_ERROR'; diff --git a/packages/redux-devtools-core/src/app/constants/socketOptions.js b/packages/redux-devtools-core/src/app/constants/socketOptions.ts similarity index 100% rename from packages/redux-devtools-core/src/app/constants/socketOptions.js rename to packages/redux-devtools-core/src/app/constants/socketOptions.ts diff --git a/packages/redux-devtools-core/src/app/containers/Actions.js b/packages/redux-devtools-core/src/app/containers/Actions.tsx similarity index 72% rename from packages/redux-devtools-core/src/app/containers/Actions.js rename to packages/redux-devtools-core/src/app/containers/Actions.tsx index 85b6a0ca..d813203c 100644 --- a/packages/redux-devtools-core/src/app/containers/Actions.js +++ b/packages/redux-devtools-core/src/app/containers/Actions.tsx @@ -1,7 +1,5 @@ import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; +import { connect, ResolveThunks } from 'react-redux'; import { Container } from 'devui'; import SliderMonitor from './monitors/Slider'; import { liftedDispatch as liftedDispatchAction, getReport } from '../actions'; @@ -10,8 +8,13 @@ import DevTools from '../containers/DevTools'; import Dispatcher from './monitors/Dispatcher'; import TopButtons from '../components/TopButtons'; import BottomButtons from '../components/BottomButtons'; +import { StoreState } from '../reducers'; -class Actions extends Component { +type StateProps = ReturnType; +type DispatchProps = ResolveThunks; +type Props = StateProps & DispatchProps; + +class Actions extends Component { render() { const { monitor, @@ -51,17 +54,7 @@ class Actions extends Component { } } -Actions.propTypes = { - liftedDispatch: PropTypes.func.isRequired, - liftedState: PropTypes.object.isRequired, - monitorState: PropTypes.object, - options: PropTypes.object.isRequired, - monitor: PropTypes.string, - dispatcherIsOpen: PropTypes.bool, - sliderIsOpen: PropTypes.bool, -}; - -function mapStateToProps(state) { +const mapStateToProps = (state: StoreState) => { const instances = state.instances; const id = getActiveInstance(instances); return { @@ -73,13 +66,11 @@ function mapStateToProps(state) { sliderIsOpen: state.monitor.sliderIsOpen, reports: state.reports.data, }; -} +}; -function mapDispatchToProps(dispatch) { - return { - liftedDispatch: bindActionCreators(liftedDispatchAction, dispatch), - getReport: bindActionCreators(getReport, dispatch), - }; -} +const actionCreators = { + liftedDispatch: liftedDispatchAction, + getReport, +}; -export default connect(mapStateToProps, mapDispatchToProps)(Actions); +export default connect(mapStateToProps, actionCreators)(Actions); diff --git a/packages/redux-devtools-core/src/app/containers/App.js b/packages/redux-devtools-core/src/app/containers/App.tsx similarity index 54% rename from packages/redux-devtools-core/src/app/containers/App.js rename to packages/redux-devtools-core/src/app/containers/App.tsx index c456800a..9417bd5c 100644 --- a/packages/redux-devtools-core/src/app/containers/App.js +++ b/packages/redux-devtools-core/src/app/containers/App.tsx @@ -1,14 +1,17 @@ import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; +import { connect, ResolveThunks } from 'react-redux'; import { Container, Notification } from 'devui'; import { clearNotification } from '../actions'; import Header from '../components/Header'; import Actions from '../containers/Actions'; import Settings from '../components/Settings'; +import { StoreState } from '../reducers'; -class App extends Component { +type StateProps = ReturnType; +type DispatchProps = ResolveThunks; +type Props = StateProps & DispatchProps; + +class App extends Component { render() { const { section, theme, notification } = this.props; let body; @@ -37,28 +40,14 @@ class App extends Component { } } -App.propTypes = { - section: PropTypes.string.isRequired, - theme: PropTypes.object.isRequired, - notification: PropTypes.shape({ - message: PropTypes.string, - type: PropTypes.string, - }), - clearNotification: PropTypes.func, +const mapStateToProps = (state: StoreState) => ({ + section: state.section, + theme: state.theme, + notification: state.notification, +}); + +const actionCreators = { + clearNotification, }; -function mapStateToProps(state) { - return { - section: state.section, - theme: state.theme, - notification: state.notification, - }; -} - -function mapDispatchToProps(dispatch) { - return { - clearNotification: bindActionCreators(clearNotification, dispatch), - }; -} - -export default connect(mapStateToProps, mapDispatchToProps)(App); +export default connect(mapStateToProps, actionCreators)(App); diff --git a/packages/redux-devtools-core/src/app/containers/DevTools.js b/packages/redux-devtools-core/src/app/containers/DevTools.tsx similarity index 53% rename from packages/redux-devtools-core/src/app/containers/DevTools.js rename to packages/redux-devtools-core/src/app/containers/DevTools.tsx index 5009a06d..cbfff2de 100644 --- a/packages/redux-devtools-core/src/app/containers/DevTools.js +++ b/packages/redux-devtools-core/src/app/containers/DevTools.tsx @@ -1,20 +1,49 @@ import React, { Component } from 'react'; -import PropTypes from 'prop-types'; import { withTheme } from 'styled-components'; +import { LiftedAction, LiftedState } from 'redux-devtools-instrument'; +import { Action } from 'redux'; import getMonitor from '../utils/getMonitor'; +import { InitMonitorAction } from '../actions'; +import { Features, State } from '../reducers/instances'; +import { MonitorStateMonitorState } from '../reducers/monitor'; +import { ThemeFromProvider } from 'devui'; -class DevTools extends Component { - constructor(props) { +interface Props { + monitor: string; + liftedState: State; + monitorState: MonitorStateMonitorState | undefined; + dispatch: ( + action: LiftedAction, unknown> | InitMonitorAction + ) => void; + features: Features | undefined; + theme: ThemeFromProvider; +} + +class DevTools extends Component { + monitorProps: unknown; + Monitor?: React.ComponentType< + LiftedState, unknown> + > & { + update( + monitorProps: unknown, + state: unknown | undefined, + action: Action + ): unknown; + }; + preventRender?: boolean; + + constructor(props: Props) { super(props); this.getMonitor(props, props.monitorState); } - getMonitor(props, skipUpdate) { + getMonitor(props: Props, skipUpdate?: unknown) { const monitorElement = getMonitor(props); this.monitorProps = monitorElement.props; this.Monitor = monitorElement.type; - const update = this.Monitor.update; + // eslint-disable-next-line @typescript-eslint/unbound-method + const update = this.Monitor!.update; if (update) { let newMonitorState; const monitorState = props.monitorState; @@ -24,7 +53,11 @@ class DevTools extends Component { ) { newMonitorState = monitorState; } else { - newMonitorState = update(this.monitorProps, undefined, {}); + newMonitorState = update( + this.monitorProps, + undefined, + {} as Action + ); if (newMonitorState !== monitorState) { this.preventRender = true; } @@ -38,21 +71,23 @@ class DevTools extends Component { } } - UNSAFE_componentWillUpdate(nextProps) { + UNSAFE_componentWillUpdate(nextProps: Props) { if (nextProps.monitor !== this.props.monitor) this.getMonitor(nextProps); } - shouldComponentUpdate(nextProps) { + shouldComponentUpdate(nextProps: Props) { return ( nextProps.monitor !== this.props.monitor || nextProps.liftedState !== this.props.liftedState || - nextProps.monitorState !== this.props.liftedState || + nextProps.monitorState !== this.props.monitorState || nextProps.features !== this.props.features || nextProps.theme.scheme !== this.props.theme.scheme ); } - dispatch = (action) => { + dispatch = ( + action: LiftedAction, unknown> | InitMonitorAction + ) => { this.props.dispatch(action); }; @@ -66,9 +101,10 @@ class DevTools extends Component { ...this.props.liftedState, monitorState: this.props.monitorState, }; + const MonitorAsAny = this.Monitor as any; return (
- ; +type Props = DispatchProps; + +class ChartMonitorWrapper extends Component { static update = ChartMonitor.update; - onClickText = (data) => { - const inspectedStatePath = []; + onClickText = (data: NodeWithId) => { + const inspectedStatePath: string[] = []; getPath(data, inspectedStatePath); this.props.selectMonitorWithState('InspectorMonitor', { inspectedStatePath, @@ -33,6 +35,8 @@ class ChartMonitorWrapper extends Component { render() { return ( + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore ; +interface OwnProps { + options: Options; +} +type Props = DispatchProps & OwnProps; - state = { +interface State { + selected: 'default' | number; + customAction: string; + args: (string | undefined)[]; + rest: string; + changed: boolean; +} + +class Dispatcher extends Component { + state: State = { selected: 'default', customAction: this.props.options.lib === 'redux' ? "{\n type: ''\n}" : 'this.', @@ -62,7 +70,7 @@ class Dispatcher extends Component { changed: false, }; - UNSAFE_componentWillReceiveProps(nextProps) { + UNSAFE_componentWillReceiveProps(nextProps: Props) { if ( this.state.selected !== 'default' && !nextProps.options.actionCreators @@ -74,14 +82,14 @@ class Dispatcher extends Component { } } - shouldComponentUpdate(nextProps, nextState) { + shouldComponentUpdate(nextProps: Props, nextState: State) { return ( nextState !== this.state || nextProps.options.actionCreators !== this.props.options.actionCreators ); } - selectActionCreator = (selected) => { + selectActionCreator = (selected: 'default' | 'actions-help' | number) => { if (selected === 'actions-help') { window.open( 'https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/' + @@ -90,14 +98,14 @@ class Dispatcher extends Component { return; } - const args = []; + const args: string[] = []; if (selected !== 'default') { - args.length = this.props.options.actionCreators[selected].args.length; + args.length = this.props.options.actionCreators![selected].args.length; } this.setState({ selected, args, rest: '[]', changed: false }); }; - handleArg = (argIndex) => (value) => { + handleArg = (argIndex: number) => (value: string) => { const args = [ ...this.state.args.slice(0, argIndex), value || undefined, @@ -106,26 +114,26 @@ class Dispatcher extends Component { this.setState({ args, changed: true }); }; - handleRest = (rest) => { + handleRest = (rest: string) => { this.setState({ rest, changed: true }); }; - handleCustomAction = (customAction) => { + handleCustomAction = (customAction: string) => { this.setState({ customAction, changed: true }); }; dispatchAction = () => { const { selected, customAction, args, rest } = this.state; - if (this.state.selected !== 'default') { + if (selected !== 'default') { // remove trailing `undefined` arguments let i = args.length - 1; while (i >= 0 && typeof args[i] === 'undefined') { - args.pop(i); + args.pop(); i--; } this.props.dispatch({ - name: this.props.options.actionCreators[selected].name, + name: this.props.options.actionCreators![selected].name, selected, args, rest, @@ -174,7 +182,9 @@ class Dispatcher extends Component { ); } - let options = [{ value: 'default', label: 'Custom action' }]; + let options: { value: string | number; label: string }[] = [ + { value: 'default', label: 'Custom action' }, + ]; if (actionCreators && actionCreators.length > 0) { options = options.concat( actionCreators.map(({ name, args }, i) => ({ @@ -208,10 +218,8 @@ class Dispatcher extends Component { } } -function mapDispatchToProps(dispatch) { - return { - dispatch: bindActionCreators(dispatchRemotely, dispatch), - }; -} +const actionCreators = { + dispatch: dispatchRemotely, +}; -export default connect(null, mapDispatchToProps)(Dispatcher); +export default connect(null, actionCreators)(Dispatcher); diff --git a/packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/ChartTab.js b/packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/ChartTab.tsx similarity index 53% rename from packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/ChartTab.js rename to packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/ChartTab.tsx index d3e4ff49..f2fc0b60 100644 --- a/packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/ChartTab.js +++ b/packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/ChartTab.tsx @@ -1,18 +1,28 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; +import React, { Component, RefCallback } from 'react'; +import { connect, ResolveThunks } from 'react-redux'; import { withTheme } from 'styled-components'; -import { tree } from 'd3-state-visualizer'; +import { InputOptions, NodeWithId, tree } from 'd3-state-visualizer'; import { getPath } from '../ChartMonitorWrapper'; import { updateMonitorState } from '../../../actions'; +import { ThemeFromProvider } from 'devui'; const style = { width: '100%', height: '100%', }; -class ChartTab extends Component { +type DispatchProps = ResolveThunks; +interface OwnProps { + data: unknown; + theme: ThemeFromProvider; +} +type Props = DispatchProps & OwnProps; + +class ChartTab extends Component { + node?: HTMLDivElement | null; + // eslint-disable-next-line @typescript-eslint/ban-types + renderChart?: (nextState?: {} | null | undefined) => void; + shouldComponentUpdate() { return false; } @@ -21,28 +31,30 @@ class ChartTab extends Component { this.createChart(this.props); } - UNSAFE_componentWillReceiveProps(nextProps) { + UNSAFE_componentWillReceiveProps(nextProps: Props) { if ( this.props.theme.scheme !== nextProps.theme.scheme || nextProps.theme.light !== this.props.theme.light ) { - this.node.innerHTML = ''; + this.node!.innerHTML = ''; this.createChart(nextProps); } else if (nextProps.data !== this.props.data) { - this.renderChart(nextProps.data); + // eslint-disable-next-line @typescript-eslint/ban-types + this.renderChart!(nextProps.data as {} | null | undefined); } } - getRef = (node) => { + getRef: RefCallback = (node) => { this.node = node; }; - createChart(props) { - this.renderChart = tree(this.node, this.getChartTheme(props.theme)); - this.renderChart(props.data); + createChart(props: Props) { + this.renderChart = tree(this.node!, this.getChartTheme(props.theme)); + // eslint-disable-next-line @typescript-eslint/ban-types + this.renderChart(props.data as {} | null | undefined); } - getChartTheme(theme) { + getChartTheme(theme: ThemeFromProvider): Partial { return { heightBetweenNodesCoeff: 1, widthBetweenNodesCoeff: 1.3, @@ -60,27 +72,27 @@ class ChartTab extends Component { style: { width: '100%', height: '100%', - node: { + node: ({ colors: { default: theme.base0B, collapsed: theme.base0B, parent: theme.base0E, }, radius: 7, - }, - text: { + } as unknown) as string, + text: ({ colors: { default: theme.base0D, hover: theme.base06, }, - }, + } as unknown) as string, }, onClickText: this.onClickText, }; } - onClickText = (data) => { - const inspectedStatePath = []; + onClickText = (data: NodeWithId) => { + const inspectedStatePath: string[] = []; getPath(data, inspectedStatePath); this.props.updateMonitorState({ inspectedStatePath, @@ -93,17 +105,9 @@ class ChartTab extends Component { } } -ChartTab.propTypes = { - data: PropTypes.object, - updateMonitorState: PropTypes.func.isRequired, - theme: PropTypes.object.isRequired, +const actionCreators = { + updateMonitorState, }; -function mapDispatchToProps(dispatch) { - return { - updateMonitorState: bindActionCreators(updateMonitorState, dispatch), - }; -} - -const ConnectedChartTab = connect(null, mapDispatchToProps)(ChartTab); +const ConnectedChartTab = connect(null, actionCreators)(ChartTab); export default withTheme(ConnectedChartTab); diff --git a/packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/RawTab.js b/packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/RawTab.tsx similarity index 57% rename from packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/RawTab.js rename to packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/RawTab.tsx index 68fd5cb5..15cead09 100644 --- a/packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/RawTab.js +++ b/packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/RawTab.tsx @@ -2,21 +2,27 @@ import React, { Component } from 'react'; import { Editor } from 'devui'; import { stringify } from 'javascript-stringify'; -export default class RawTab extends Component { - constructor(props) { +interface Props { + data: unknown; +} + +export default class RawTab extends Component { + value?: string | undefined; + + constructor(props: Props) { super(props); this.stringifyData(props); } - shouldComponentUpdate(nextProps) { + shouldComponentUpdate(nextProps: Props) { return nextProps.data !== this.value; } - UNSAFE_componentWillUpdate(nextProps) { + UNSAFE_componentWillUpdate(nextProps: Props) { this.stringifyData(nextProps); } - stringifyData(props) { + stringifyData(props: Props) { this.value = stringify(props.data, null, 2); } diff --git a/packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/SubTabs.js b/packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/SubTabs.tsx similarity index 62% rename from packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/SubTabs.js rename to packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/SubTabs.tsx index f5d31cde..a70b2eae 100644 --- a/packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/SubTabs.js +++ b/packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/SubTabs.tsx @@ -1,8 +1,8 @@ import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { bindActionCreators } from 'redux'; -import { connect } from 'react-redux'; -import { Tabs } from 'devui'; +import { connect, ResolveThunks } from 'react-redux'; +import { Tab, Tabs } from 'devui'; +import { TabComponentProps } from 'redux-devtools-inspector-monitor'; +import { Action } from 'redux'; import StateTree from 'redux-devtools-inspector-monitor/lib/tabs/StateTab'; import ActionTree from 'redux-devtools-inspector-monitor/lib/tabs/ActionTab'; import DiffTree from 'redux-devtools-inspector-monitor/lib/tabs/DiffTab'; @@ -10,14 +10,24 @@ import { selectMonitorTab } from '../../../actions'; import RawTab from './RawTab'; import ChartTab from './ChartTab'; import VisualDiffTab from './VisualDiffTab'; +import { StoreState } from '../../../reducers'; +import { Delta } from 'jsondiffpatch'; -class SubTabs extends Component { - constructor(props) { +type StateProps = ReturnType; +type DispatchProps = ResolveThunks; +type Props = StateProps & + DispatchProps & + TabComponentProps>; + +class SubTabs extends Component { + tabs?: (Tab | Tab<{ data: unknown }> | Tab<{ data?: Delta }>)[]; + + constructor(props: Props) { super(props); this.updateTabs(props); } - UNSAFE_componentWillReceiveProps(nextProps) { + UNSAFE_componentWillReceiveProps(nextProps: Props) { if (nextProps.parentTab !== this.props.parentTab) { this.updateTabs(nextProps); } @@ -34,7 +44,7 @@ class SubTabs extends Component { } }; - updateTabs(props) { + updateTabs(props: Props) { const parentTab = props.parentTab; if (parentTab === 'Diff') { @@ -47,7 +57,7 @@ class SubTabs extends Component { { name: 'Raw', component: VisualDiffTab, - selector: this.selector, + selector: this.selector as () => { data?: Delta }, }, ]; return; @@ -79,7 +89,7 @@ class SubTabs extends Component { return ( @@ -87,26 +97,13 @@ class SubTabs extends Component { } } -SubTabs.propTypes = { - selected: PropTypes.string, - parentTab: PropTypes.string, - selectMonitorTab: PropTypes.func.isRequired, - action: PropTypes.object, - delta: PropTypes.object, - nextState: PropTypes.object, +const mapStateToProps = (state: StoreState) => ({ + parentTab: state.monitor.monitorState!.tabName, + selected: state.monitor.monitorState!.subTabName, +}); + +const actionCreators = { + selectMonitorTab, }; -function mapStateToProps(state) { - return { - parentTab: state.monitor.monitorState.tabName, - selected: state.monitor.monitorState.subTabName, - }; -} - -function mapDispatchToProps(dispatch) { - return { - selectMonitorTab: bindActionCreators(selectMonitorTab, dispatch), - }; -} - -export default connect(mapStateToProps, mapDispatchToProps)(SubTabs); +export default connect(mapStateToProps, actionCreators)(SubTabs); diff --git a/packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/VisualDiffTab.js b/packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/VisualDiffTab.tsx similarity index 93% rename from packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/VisualDiffTab.js rename to packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/VisualDiffTab.tsx index d2fb3d49..f48e0c55 100644 --- a/packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/VisualDiffTab.js +++ b/packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/VisualDiffTab.tsx @@ -1,6 +1,5 @@ import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { formatters } from 'jsondiffpatch'; +import { Delta, formatters } from 'jsondiffpatch'; import styled from 'styled-components'; import { effects } from 'devui'; @@ -218,22 +217,22 @@ export const StyledContainer = styled.div` } `; -export default class VisualDiffTab extends Component { - shouldComponentUpdate(nextProps) { +interface Props { + data?: Delta; +} + +export default class VisualDiffTab extends Component { + shouldComponentUpdate(nextProps: Props) { return this.props.data !== nextProps.data; } render() { - let __html; + let __html: string | undefined; const data = this.props.data; if (data) { - __html = formatters.html.format(data); + __html = formatters.html.format(data, undefined); } - return ; + return ; } } - -VisualDiffTab.propTypes = { - data: PropTypes.object, -}; diff --git a/packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/index.js b/packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/index.tsx similarity index 58% rename from packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/index.js rename to packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/index.tsx index f8ebd4cc..63239de8 100644 --- a/packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/index.js +++ b/packages/redux-devtools-core/src/app/containers/monitors/InspectorWrapper/index.tsx @@ -1,10 +1,10 @@ import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import InspectorMonitor from 'redux-devtools-inspector-monitor'; +import InspectorMonitor, { Tab } from 'redux-devtools-inspector-monitor'; import TraceTab from 'redux-devtools-inspector-monitor-trace-tab'; import TestTab from 'redux-devtools-inspector-monitor-test-tab'; import { DATA_TYPE_KEY } from '../../../constants/dataTypes'; import SubTabs from './SubTabs'; +import { Action } from 'redux'; const DEFAULT_TABS = [ { @@ -25,25 +25,38 @@ const DEFAULT_TABS = [ }, ]; -class InspectorWrapper extends Component { +interface Features { + test?: boolean; + skip?: boolean; +} +interface Props { + features?: Features; +} + +class InspectorWrapper extends Component { static update = InspectorMonitor.update; render() { const { features, ...rest } = this.props; - let tabs; + let tabs: () => Tab>[]; if (features && features.test) { - tabs = () => [...DEFAULT_TABS, { name: 'Test', component: TestTab }]; + tabs = () => [ + ...(DEFAULT_TABS as Tab>[]), + ({ name: 'Test', component: TestTab } as unknown) as Tab< + unknown, + Action + >, + ]; } else { - tabs = () => DEFAULT_TABS; + tabs = () => DEFAULT_TABS as Tab>[]; } return ( @@ -51,8 +64,4 @@ class InspectorWrapper extends Component { } } -InspectorWrapper.propTypes = { - features: PropTypes.object, -}; - export default InspectorWrapper; diff --git a/packages/redux-devtools-core/src/app/containers/monitors/Slider.js b/packages/redux-devtools-core/src/app/containers/monitors/Slider.tsx similarity index 58% rename from packages/redux-devtools-core/src/app/containers/monitors/Slider.js rename to packages/redux-devtools-core/src/app/containers/monitors/Slider.tsx index 8363108c..03a5fe4f 100644 --- a/packages/redux-devtools-core/src/app/containers/monitors/Slider.js +++ b/packages/redux-devtools-core/src/app/containers/monitors/Slider.tsx @@ -1,7 +1,10 @@ import React, { Component } from 'react'; -import PropTypes from 'prop-types'; import styled, { withTheme } from 'styled-components'; import SliderMonitor from 'redux-devtools-slider-monitor'; +import { LiftedAction } from 'redux-devtools-instrument'; +import { Action } from 'redux'; +import { ThemeFromProvider } from 'devui'; +import { State } from '../../reducers/instances'; const SliderWrapper = styled.div` border-color: ${(props) => props.theme.base02}; @@ -9,8 +12,14 @@ const SliderWrapper = styled.div` border-width: 1px 0; `; -class Slider extends Component { - shouldComponentUpdate(nextProps) { +interface Props { + liftedState: State; + dispatch: (action: LiftedAction, unknown>) => void; + theme: ThemeFromProvider; +} + +class Slider extends Component { + shouldComponentUpdate(nextProps: Props) { return ( nextProps.liftedState !== this.props.liftedState || nextProps.theme.scheme !== this.props.theme.scheme @@ -21,6 +30,8 @@ class Slider extends Component { { + store?: Store; -class Root extends Component { UNSAFE_componentWillMount() { configureStore((store, preloadedState) => { this.store = store; store.dispatch({ type: CONNECT_REQUEST, - options: preloadedState.connection || this.props.socketOptions, + options: (preloadedState!.connection || + this.props.socketOptions) as ConnectionOptions, }); this.forceUpdate(); }); @@ -20,21 +29,13 @@ class Root extends Component { render() { if (!this.store) return null; + const AppAsAny = App as any; return ( - + ); } } -Root.propTypes = { - socketOptions: PropTypes.shape({ - hostname: PropTypes.string, - port: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - autoReconnect: PropTypes.bool, - secure: PropTypes.bool, - }), -}; - export default Root; diff --git a/packages/redux-devtools-core/src/app/middlewares/api.js b/packages/redux-devtools-core/src/app/middlewares/api.ts similarity index 70% rename from packages/redux-devtools-core/src/app/middlewares/api.js rename to packages/redux-devtools-core/src/app/middlewares/api.ts index 7a6bedd4..bc303987 100644 --- a/packages/redux-devtools-core/src/app/middlewares/api.js +++ b/packages/redux-devtools-core/src/app/middlewares/api.ts @@ -1,5 +1,6 @@ -import socketCluster from 'socketcluster-client'; +import socketCluster, { SCClientSocket } from 'socketcluster-client'; import { stringify } from 'jsan'; +import { Dispatch, MiddlewareAPI } from 'redux'; import socketOptions from '../constants/socketOptions'; import * as actions from '../constants/socketActionTypes'; import { getActiveInstance } from '../reducers/instances'; @@ -12,22 +13,37 @@ import { GET_REPORT_ERROR, GET_REPORT_SUCCESS, } from '../constants/actionTypes'; -import { showNotification, importState } from '../actions'; +import { + showNotification, + importState, + StoreAction, + EmitAction, + LiftedActionAction, + Request, + DispatchAction, + UpdateReportsRequest, +} from '../actions'; import { nonReduxDispatch } from '../utils/monitorActions'; +import { StoreState } from '../reducers'; -let socket; -let store; +let socket: SCClientSocket; +let store: MiddlewareAPI, StoreState>; -function emit({ message: type, id, instanceId, action, state }) { +function emit({ message: type, id, instanceId, action, state }: EmitAction) { socket.emit(id ? 'sc-' + id : 'respond', { type, action, state, instanceId }); } -function startMonitoring(channel) { +function startMonitoring(channel: string) { if (channel !== store.getState().socket.baseChannel) return; store.dispatch({ type: actions.EMIT, message: 'START' }); } -function dispatchRemoteAction({ message, action, state, toAll }) { +function dispatchRemoteAction({ + message, + action, + state, + toAll, +}: LiftedActionAction) { const instances = store.getState().instances; const instanceId = getActiveInstance(instances); const id = !toAll && instances.options[instanceId].connectionId; @@ -39,7 +55,7 @@ function dispatchRemoteAction({ message, action, state, toAll }) { store, message, instanceId, - action, + action as DispatchAction, state, instances ), @@ -48,7 +64,32 @@ function dispatchRemoteAction({ message, action, state, toAll }) { }); } -function monitoring(request) { +interface RequestBase { + id?: string; + instanceId?: string; +} +interface DisconnectedAction extends RequestBase { + type: 'DISCONNECTED'; + id: string; +} +interface StartAction extends RequestBase { + type: 'START'; + id: string; +} +interface ErrorAction extends RequestBase { + type: 'ERROR'; + payload: string; +} +interface RequestWithData extends RequestBase { + data: Request; +} +type MonitoringRequest = + | DisconnectedAction + | StartAction + | ErrorAction + | Request; + +function monitoring(request: MonitoringRequest) { if (request.type === 'DISCONNECTED') { store.dispatch({ type: REMOVE_INSTANCE, @@ -68,7 +109,9 @@ function monitoring(request) { store.dispatch({ type: UPDATE_STATE, - request: request.data ? { ...request.data, id: request.id } : request, + request: ((request as unknown) as RequestWithData).data + ? { ...((request as unknown) as RequestWithData).data, id: request.id } + : request, }); const instances = store.getState().instances; @@ -87,11 +130,14 @@ function monitoring(request) { } } -function subscribe(channelName, subscription) { +function subscribe( + channelName: string, + subscription: typeof UPDATE_STATE | typeof UPDATE_REPORTS +) { const channel = socket.subscribe(channelName); if (subscription === UPDATE_STATE) channel.watch(monitoring); else { - const watcher = (request) => { + const watcher = (request: UpdateReportsRequest) => { store.dispatch({ type: subscription, request }); }; channel.watch(watcher); @@ -150,7 +196,7 @@ function connect() { socket = socketCluster.create( connection.type === 'remotedev' ? socketOptions : connection.options ); - handleConnection(store); + handleConnection(); } catch (error) { store.dispatch({ type: actions.CONNECT_ERROR, error }); store.dispatch(showNotification(error.message || error)); @@ -163,7 +209,7 @@ function disconnect() { } function login() { - socket.emit('login', {}, (error, baseChannel) => { + socket.emit('login', {}, (error: Error, baseChannel: string) => { if (error) { store.dispatch({ type: actions.AUTH_ERROR, error }); return; @@ -182,24 +228,28 @@ function login() { }); } -function getReport(reportId) { - socket.emit('getReport', reportId, (error, data) => { - if (error) { - store.dispatch({ type: GET_REPORT_ERROR, error }); - return; +function getReport(reportId: unknown) { + socket.emit( + 'getReport', + reportId, + (error: Error, data: { payload: string }) => { + if (error) { + store.dispatch({ type: GET_REPORT_ERROR, error }); + return; + } + store.dispatch({ type: GET_REPORT_SUCCESS, data }); + store.dispatch(importState(data.payload)); } - store.dispatch({ type: GET_REPORT_SUCCESS, data }); - store.dispatch(importState(data.payload)); - }); + ); } -export default function api(inStore) { +export default function api( + inStore: MiddlewareAPI, StoreState> +) { store = inStore; - return (next) => (action) => { + return (next: Dispatch) => (action: StoreAction) => { const result = next(action); - switch ( - action.type // eslint-disable-line default-case - ) { + switch (action.type) { case actions.CONNECT_REQUEST: connect(); break; diff --git a/packages/redux-devtools-core/src/app/middlewares/exportState.js b/packages/redux-devtools-core/src/app/middlewares/exportState.ts similarity index 72% rename from packages/redux-devtools-core/src/app/middlewares/exportState.js rename to packages/redux-devtools-core/src/app/middlewares/exportState.ts index d0609022..58cb9315 100644 --- a/packages/redux-devtools-core/src/app/middlewares/exportState.js +++ b/packages/redux-devtools-core/src/app/middlewares/exportState.ts @@ -1,14 +1,17 @@ import stringifyJSON from '../utils/stringifyJSON'; import { UPDATE_STATE, LIFTED_ACTION, EXPORT } from '../constants/actionTypes'; import { getActiveInstance } from '../reducers/instances'; +import { Dispatch, MiddlewareAPI } from 'redux'; +import { ExportRequest, StoreAction } from '../actions'; +import { StoreState } from '../reducers'; -let toExport; +let toExport: string | undefined; -function download(state) { +function download(state: string) { const blob = new Blob([state], { type: 'octet/stream' }); const href = window.URL.createObjectURL(blob); const a = document.createElement('a'); - a.style = 'display: none'; + a.style.display = 'none'; a.download = 'state.json'; a.href = href; document.body.appendChild(a); @@ -19,15 +22,17 @@ function download(state) { }, 0); } -const exportState = (store) => (next) => (action) => { +const exportState = ( + store: MiddlewareAPI, StoreState> +) => (next: Dispatch) => (action: StoreAction) => { const result = next(action); if ( toExport && action.type === UPDATE_STATE && - action.request.type === 'EXPORT' + action.request!.type === 'EXPORT' ) { - const request = action.request; + const request = action.request!; const id = request.instanceId || request.id; if (id === toExport) { toExport = undefined; @@ -35,7 +40,7 @@ const exportState = (store) => (next) => (action) => { JSON.stringify( { payload: request.payload, - preloadedState: request.committedState, + preloadedState: (request as ExportRequest).committedState, }, null, '\t' diff --git a/packages/redux-devtools-core/src/app/reducers/connection.js b/packages/redux-devtools-core/src/app/reducers/connection.ts similarity index 50% rename from packages/redux-devtools-core/src/app/reducers/connection.js rename to packages/redux-devtools-core/src/app/reducers/connection.ts index a9e64411..65c609a8 100644 --- a/packages/redux-devtools-core/src/app/reducers/connection.js +++ b/packages/redux-devtools-core/src/app/reducers/connection.ts @@ -1,11 +1,22 @@ import { RECONNECT } from '../constants/socketActionTypes'; +import { ConnectionType, StoreAction } from '../actions'; + +export interface ConnectionOptions { + readonly hostname: string; + readonly port: number; + readonly secure: boolean; +} +export interface ConnectionState { + readonly options: ConnectionOptions; + readonly type: ConnectionType; +} export default function connection( - state = { + state: ConnectionState = { options: { hostname: 'localhost', port: 8000, secure: false }, type: 'remotedev', }, - action + action: StoreAction ) { if (action.type === RECONNECT) { const { type, ...options } = action.options; diff --git a/packages/redux-devtools-core/src/app/reducers/index.js b/packages/redux-devtools-core/src/app/reducers/index.js deleted file mode 100644 index ae099180..00000000 --- a/packages/redux-devtools-core/src/app/reducers/index.js +++ /dev/null @@ -1,22 +0,0 @@ -import { combineReducers } from 'redux'; -import section from './section'; -import connection from './connection'; -import socket from './socket'; -import monitor from './monitor'; -import notification from './notification'; -import instances from './instances'; -import reports from './reports'; -import theme from './theme'; - -const rootReducer = combineReducers({ - section, - theme, - connection, - socket, - monitor, - instances, - reports, - notification, -}); - -export default rootReducer; diff --git a/packages/redux-devtools-core/src/app/reducers/index.ts b/packages/redux-devtools-core/src/app/reducers/index.ts new file mode 100644 index 00000000..6b47effd --- /dev/null +++ b/packages/redux-devtools-core/src/app/reducers/index.ts @@ -0,0 +1,34 @@ +import { combineReducers } from 'redux'; +import section, { SectionState } from './section'; +import connection, { ConnectionState } from './connection'; +import socket, { SocketState } from './socket'; +import monitor, { MonitorState } from './monitor'; +import notification, { NotificationState } from './notification'; +import instances, { InstancesState } from './instances'; +import reports, { ReportsState } from './reports'; +import theme, { ThemeState } from './theme'; +import { StoreAction } from '../actions'; + +export interface StoreState { + readonly section: SectionState; + readonly theme: ThemeState; + readonly connection: ConnectionState; + readonly socket: SocketState; + readonly monitor: MonitorState; + readonly instances: InstancesState; + readonly reports: ReportsState; + readonly notification: NotificationState; +} + +const rootReducer = combineReducers({ + section, + theme, + connection, + socket, + monitor, + instances, + reports, + notification, +}); + +export default rootReducer; diff --git a/packages/redux-devtools-core/src/app/reducers/instances.js b/packages/redux-devtools-core/src/app/reducers/instances.ts similarity index 76% rename from packages/redux-devtools-core/src/app/reducers/instances.js rename to packages/redux-devtools-core/src/app/reducers/instances.ts index 519e750a..aa98f2c3 100644 --- a/packages/redux-devtools-core/src/app/reducers/instances.js +++ b/packages/redux-devtools-core/src/app/reducers/instances.ts @@ -1,3 +1,5 @@ +import { PerformAction } from 'redux-devtools-instrument'; +import { Action } from 'redux'; import { UPDATE_STATE, SET_STATE, @@ -10,8 +12,60 @@ import { import { DISCONNECTED } from '../constants/socketActionTypes'; import parseJSON from '../utils/parseJSON'; import { recompute } from '../utils/updateState'; +import { + ActionCreator, + LiftedActionDispatchAction, + Request, + StoreAction, +} from '../actions'; -export const initialState = { +export interface Features { + lock?: boolean; + export?: string | boolean; + import?: string | boolean; + persist?: boolean; + pause?: boolean; + reorder?: boolean; + jump?: boolean; + skip?: boolean; + dispatch?: boolean; + sync?: boolean; + test?: boolean; +} + +export interface Options { + name?: string; + connectionId?: string; + explicitLib?: string; + lib?: string; + actionCreators?: ActionCreator[]; + features: Features; + serialize?: boolean; +} + +export interface State { + actionsById: { [actionId: number]: PerformAction> }; + computedStates: { state: unknown; error?: string }[]; + currentStateIndex: number; + nextActionId: number; + skippedActionIds: number[]; + stagedActionIds: number[]; + committedState?: unknown; + isLocked?: boolean; + isPaused?: boolean; +} + +export interface InstancesState { + selected: string | null; + current: string; + sync: boolean; + connections: { [id: string]: string[] }; + options: { [id: string]: Options }; + states: { [id: string]: State }; + persisted?: boolean; +} + +export const initialState: InstancesState = { selected: null, current: 'default', sync: false, @@ -29,35 +83,42 @@ export const initialState = { }, }; -function updateState(state, request, id, serialize) { - let payload = request.payload; +function updateState( + state: { [id: string]: State }, + request: Request, + id: string, + serialize: boolean | undefined +) { + let payload: State = request.payload as State; const actionsById = request.actionsById; if (actionsById) { payload = { + // eslint-disable-next-line @typescript-eslint/ban-types ...payload, actionsById: parseJSON(actionsById, serialize), computedStates: parseJSON(request.computedStates, serialize), - }; + } as State; if (request.type === 'STATE' && request.committedState) { payload.committedState = payload.computedStates[0].state; } } else { - payload = parseJSON(payload, serialize); + payload = parseJSON((payload as unknown) as string, serialize) as State; } let newState; const liftedState = state[id] || state.default; - const action = (request.action && parseJSON(request.action, serialize)) || {}; + const action = ((request.action && parseJSON(request.action, serialize)) || + {}) as PerformAction>; switch (request.type) { case 'INIT': newState = recompute(state.default, payload, { action: { type: '@@INIT' }, - timestamp: action.timestamp || Date.now(), + timestamp: (action as { timestamp?: number }).timestamp || Date.now(), }); break; case 'ACTION': { - let isExcess = request.isExcess; + const isExcess = request.isExcess; const nextActionId = request.nextActionId || liftedState.nextActionId + 1; const maxAge = request.maxAge; if (Array.isArray(action)) { @@ -66,7 +127,7 @@ function updateState(state, request, id, serialize) { for (let i = 0; i < action.length; i++) { newState = recompute( newState, - request.batched ? payload : payload[i], + request.batched ? payload : ((payload as unknown) as State[])[i], action[i], newState.nextActionId + 1, maxAge, @@ -148,7 +209,10 @@ function updateState(state, request, id, serialize) { return { ...state, [id]: newState }; } -export function dispatchAction(state, { action }) { +export function dispatchAction( + state: InstancesState, + { action }: LiftedActionDispatchAction +) { if (action.type === 'JUMP_TO_STATE' || action.type === 'JUMP_TO_ACTION') { const id = state.selected || state.current; const liftedState = state.states[id]; @@ -167,7 +231,7 @@ export function dispatchAction(state, { action }) { return state; } -function removeState(state, connectionId) { +function removeState(state: InstancesState, connectionId: string) { const instanceIds = state.connections[connectionId]; if (!instanceIds) return state; @@ -202,7 +266,11 @@ function removeState(state, connectionId) { }; } -function init({ type, action, name, libConfig = {} }, connectionId, current) { +function init( + { type, action, name, libConfig = {} }: Request, + connectionId: string, + current: string +): Options { let lib; let actionCreators; let creators = libConfig.actionCreators || action; @@ -234,7 +302,10 @@ function init({ type, action, name, libConfig = {} }, connectionId, current) { }; } -export default function instances(state = initialState, action) { +export default function instances( + state = initialState, + action: StoreAction +): InstancesState { switch (action.type) { case UPDATE_STATE: { const { request } = action; @@ -293,7 +364,7 @@ export default function instances(state = initialState, action) { ...state, states: { ...state.states, - [id]: parseJSON(action.state), + [id]: parseJSON(action.state) as State, }, }; } @@ -307,7 +378,5 @@ export default function instances(state = initialState, action) { } } -/* eslint-disable no-shadow */ -export const getActiveInstance = (instances) => +export const getActiveInstance = (instances: InstancesState) => instances.selected || instances.current; -/* eslint-enable */ diff --git a/packages/redux-devtools-core/src/app/reducers/monitor.js b/packages/redux-devtools-core/src/app/reducers/monitor.ts similarity index 62% rename from packages/redux-devtools-core/src/app/reducers/monitor.js rename to packages/redux-devtools-core/src/app/reducers/monitor.ts index f07e2197..8293d847 100644 --- a/packages/redux-devtools-core/src/app/reducers/monitor.js +++ b/packages/redux-devtools-core/src/app/reducers/monitor.ts @@ -5,28 +5,50 @@ import { TOGGLE_SLIDER, TOGGLE_DISPATCHER, } from '../constants/actionTypes'; +import { MonitorActionAction, StoreAction } from '../actions'; -const initialState = { +export interface MonitorStateMonitorState { + inspectedStatePath?: string[]; + tabName?: string; + subTabName?: string; + selectedActionId?: number | null; + startActionId?: number | null; + inspectedActionPath?: string[]; + __overwritten__?: string; +} +export interface MonitorState { + selected: string; + monitorState: MonitorStateMonitorState | undefined; + sliderIsOpen: boolean; + dispatcherIsOpen: boolean; +} + +const initialState: MonitorState = { selected: 'InspectorMonitor', monitorState: undefined, sliderIsOpen: true, dispatcherIsOpen: false, }; -export function dispatchMonitorAction(state, action) { +export function dispatchMonitorAction( + state: MonitorState, + action: MonitorActionAction +): MonitorState { return { ...state, - monitorState: - action.action.newMonitorState || + monitorState: (action.action.newMonitorState || action.monitorReducer( action.monitorProps, state.monitorState, action.action - ), + )) as MonitorStateMonitorState, }; } -export default function monitor(state = initialState, action) { +export default function monitor( + state = initialState, + action: StoreAction +): MonitorState { switch (action.type) { case MONITOR_ACTION: return dispatchMonitorAction(state, action); @@ -45,7 +67,7 @@ export default function monitor(state = initialState, action) { }; } case UPDATE_MONITOR_STATE: { - let inspectedStatePath = state.monitorState.inspectedStatePath; + let inspectedStatePath = state.monitorState!.inspectedStatePath!; if (action.nextState.inspectedStatePath) { inspectedStatePath = [ ...inspectedStatePath.slice(0, -1), diff --git a/packages/redux-devtools-core/src/app/reducers/notification.js b/packages/redux-devtools-core/src/app/reducers/notification.ts similarity index 57% rename from packages/redux-devtools-core/src/app/reducers/notification.js rename to packages/redux-devtools-core/src/app/reducers/notification.ts index cf2e59f1..c42e1ee9 100644 --- a/packages/redux-devtools-core/src/app/reducers/notification.js +++ b/packages/redux-devtools-core/src/app/reducers/notification.ts @@ -4,8 +4,18 @@ import { LIFTED_ACTION, ERROR, } from '../constants/actionTypes'; +import { StoreAction } from '../actions'; -export default function notification(state = null, action) { +interface Notification { + readonly type: 'error'; + readonly message: string; +} +export type NotificationState = Notification | null; + +export default function notification( + state: NotificationState = null, + action: StoreAction +): NotificationState { switch (action.type) { case SHOW_NOTIFICATION: return action.notification; diff --git a/packages/redux-devtools-core/src/app/reducers/reports.js b/packages/redux-devtools-core/src/app/reducers/reports.ts similarity index 67% rename from packages/redux-devtools-core/src/app/reducers/reports.js rename to packages/redux-devtools-core/src/app/reducers/reports.ts index 5f825572..a085461c 100644 --- a/packages/redux-devtools-core/src/app/reducers/reports.js +++ b/packages/redux-devtools-core/src/app/reducers/reports.ts @@ -1,12 +1,24 @@ import { UPDATE_REPORTS /* , GET_REPORT_SUCCESS */, } from '../constants/actionTypes'; +import { StoreAction } from '../actions'; -const initialState = { +export interface Data { + id: unknown; +} + +export interface ReportsState { + data: Data[]; +} + +const initialState: ReportsState = { data: [], }; -export default function reports(state = initialState, action) { +export default function reports( + state = initialState, + action: StoreAction +): ReportsState { /* if (action.type === GET_REPORT_SUCCESS) { const id = action.data.id; return { @@ -19,17 +31,16 @@ export default function reports(state = initialState, action) { return state; const request = action.request; - const data = request.data; switch (request.type) { case 'list': return { ...state, - data, + data: request.data, }; case 'add': return { ...state, - data: [...state.data, data], + data: [...state.data, request.data], }; case 'remove': return { diff --git a/packages/redux-devtools-core/src/app/reducers/section.js b/packages/redux-devtools-core/src/app/reducers/section.js deleted file mode 100644 index 252bdfbb..00000000 --- a/packages/redux-devtools-core/src/app/reducers/section.js +++ /dev/null @@ -1,8 +0,0 @@ -import { CHANGE_SECTION } from '../constants/actionTypes'; - -export default function section(state = 'Actions', action) { - if (action.type === CHANGE_SECTION) { - return action.section; - } - return state; -} diff --git a/packages/redux-devtools-core/src/app/reducers/section.ts b/packages/redux-devtools-core/src/app/reducers/section.ts new file mode 100644 index 00000000..3497384f --- /dev/null +++ b/packages/redux-devtools-core/src/app/reducers/section.ts @@ -0,0 +1,11 @@ +import { CHANGE_SECTION } from '../constants/actionTypes'; +import { StoreAction } from '../actions'; + +export type SectionState = string; + +export default function section(state = 'Actions', action: StoreAction) { + if (action.type === CHANGE_SECTION) { + return action.section; + } + return state; +} diff --git a/packages/redux-devtools-core/src/app/reducers/socket.js b/packages/redux-devtools-core/src/app/reducers/socket.ts similarity index 72% rename from packages/redux-devtools-core/src/app/reducers/socket.js rename to packages/redux-devtools-core/src/app/reducers/socket.ts index 9d1e9192..ae8fc1b3 100644 --- a/packages/redux-devtools-core/src/app/reducers/socket.js +++ b/packages/redux-devtools-core/src/app/reducers/socket.ts @@ -1,15 +1,29 @@ +import { AuthStates, States } from 'socketcluster-client/lib/scclientsocket'; import * as actions from '../constants/socketActionTypes'; +import { StoreAction } from '../actions'; -const initialState = { +export interface SocketState { + id: string | null; + channels: string[]; + socketState: States; + authState: AuthStates | 'pending'; + error: Error | undefined; + baseChannel?: string; + authToken?: null; +} + +const initialState: SocketState = { id: null, channels: [], socketState: actions.CLOSED, authState: actions.PENDING, - authToken: null, error: undefined, }; -export default function socket(state = initialState, action) { +export default function socket( + state = initialState, + action: StoreAction +): SocketState { switch (action.type) { case actions.CONNECT_REQUEST: { return { @@ -39,7 +53,6 @@ export default function socket(state = initialState, action) { return { ...state, authState: actions.AUTHENTICATED, - authToken: action.authToken, baseChannel: action.baseChannel, }; case actions.AUTH_ERROR: @@ -57,13 +70,13 @@ export default function socket(state = initialState, action) { case actions.SUBSCRIBE_SUCCESS: return { ...state, - channels: [...state.channels, action.channelName], + channels: [...state.channels, action.channel], }; case actions.UNSUBSCRIBE: return { ...state, channels: state.channels.filter( - (channel) => channel !== action.channelName + (channel) => channel !== action.channel ), }; case actions.DISCONNECTED: diff --git a/packages/redux-devtools-core/src/app/reducers/theme.js b/packages/redux-devtools-core/src/app/reducers/theme.js deleted file mode 100644 index 1fb50905..00000000 --- a/packages/redux-devtools-core/src/app/reducers/theme.js +++ /dev/null @@ -1,15 +0,0 @@ -import { CHANGE_THEME } from '../constants/actionTypes'; - -export default function theme( - state = { theme: 'default', scheme: 'default', light: true }, - action -) { - if (action.type === CHANGE_THEME) { - return { - theme: action.theme, - scheme: action.scheme, - light: !action.dark, - }; - } - return state; -} diff --git a/packages/redux-devtools-core/src/app/reducers/theme.ts b/packages/redux-devtools-core/src/app/reducers/theme.ts new file mode 100644 index 00000000..1c6618ad --- /dev/null +++ b/packages/redux-devtools-core/src/app/reducers/theme.ts @@ -0,0 +1,27 @@ +import { Scheme, Theme } from 'devui'; +import { CHANGE_THEME } from '../constants/actionTypes'; +import { StoreAction } from '../actions'; + +export interface ThemeState { + readonly theme: Theme; + readonly scheme: Scheme; + readonly light: boolean; +} + +export default function theme( + state: ThemeState = { + theme: 'default' as const, + scheme: 'default' as const, + light: true, + }, + action: StoreAction +) { + if (action.type === CHANGE_THEME) { + return { + theme: action.theme, + scheme: action.scheme, + light: !action.dark, + }; + } + return state; +} diff --git a/packages/redux-devtools-core/src/app/store/configureStore.js b/packages/redux-devtools-core/src/app/store/configureStore.js deleted file mode 100644 index 9bcb0c1b..00000000 --- a/packages/redux-devtools-core/src/app/store/configureStore.js +++ /dev/null @@ -1,41 +0,0 @@ -import { createStore, compose, applyMiddleware } from 'redux'; -import localForage from 'localforage'; -import { getStoredState, createPersistor } from 'redux-persist'; -import api from '../middlewares/api'; -import exportState from '../middlewares/exportState'; -import rootReducer from '../reducers'; - -export default function configureStore(callback, key) { - const persistConfig = { - keyPrefix: `redux-devtools${key || ''}:`, - blacklist: ['instances', 'socket'], - storage: localForage, - serialize: (data) => data, - deserialize: (data) => data, - }; - - getStoredState(persistConfig, (err, restoredState) => { - let composeEnhancers = compose; - if (process.env.NODE_ENV !== 'production') { - if (window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) { - composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__; - } - if (module.hot) { - // Enable Webpack hot module replacement for reducers - module.hot.accept('../reducers', () => { - const nextReducer = require('../reducers'); // eslint-disable-line global-require - store.replaceReducer(nextReducer); - }); - } - } - - const store = createStore( - rootReducer, - restoredState, - composeEnhancers(applyMiddleware(exportState, api)) - ); - const persistor = createPersistor(store, persistConfig); - callback(store, restoredState); - if (err) persistor.purge(); - }); -} diff --git a/packages/redux-devtools-core/src/app/store/configureStore.ts b/packages/redux-devtools-core/src/app/store/configureStore.ts new file mode 100644 index 00000000..fc8be313 --- /dev/null +++ b/packages/redux-devtools-core/src/app/store/configureStore.ts @@ -0,0 +1,60 @@ +import { createStore, compose, applyMiddleware, Store } from 'redux'; +import localForage from 'localforage'; +import { + getStoredState, + createPersistor, + PersistorConfig, +} from 'redux-persist'; +import api from '../middlewares/api'; +import exportState from '../middlewares/exportState'; +import rootReducer, { StoreState } from '../reducers'; +import { StoreAction } from '../actions'; + +export default function configureStore( + callback: ( + store: Store, + restoredState: Partial | undefined + ) => void, + key?: string +) { + const persistConfig: PersistorConfig = ({ + keyPrefix: `redux-devtools${key || ''}:`, + blacklist: ['instances', 'socket'], + storage: localForage, + serialize: (data: unknown) => data, + deserialize: (data: unknown) => data, + } as unknown) as PersistorConfig; + + // eslint-disable-next-line @typescript-eslint/no-floating-promises + getStoredState(persistConfig, (err, restoredState) => { + let composeEnhancers = compose; + if (process.env.NODE_ENV !== 'production') { + if ( + ((window as unknown) as { + __REDUX_DEVTOOLS_EXTENSION_COMPOSE__?: typeof compose; + }).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ + ) { + composeEnhancers = ((window as unknown) as { + __REDUX_DEVTOOLS_EXTENSION_COMPOSE__: typeof compose; + }).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__; + } + if (module.hot) { + // Enable Webpack hot module replacement for reducers + module.hot.accept('../reducers', () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const nextReducer = require('../reducers'); // eslint-disable-line global-require + store.replaceReducer(nextReducer); + }); + } + } + + const store = createStore( + rootReducer, + restoredState, + composeEnhancers(applyMiddleware(exportState, api)) + ); + const persistor = createPersistor(store, persistConfig); + callback(store, restoredState); + if (err) persistor.purge(); + }); +} diff --git a/packages/redux-devtools-core/src/app/utils/commitExcessActions.js b/packages/redux-devtools-core/src/app/utils/commitExcessActions.ts similarity index 89% rename from packages/redux-devtools-core/src/app/utils/commitExcessActions.js rename to packages/redux-devtools-core/src/app/utils/commitExcessActions.ts index 76b5c15f..ff7f8fdb 100644 --- a/packages/redux-devtools-core/src/app/utils/commitExcessActions.js +++ b/packages/redux-devtools-core/src/app/utils/commitExcessActions.ts @@ -1,7 +1,9 @@ // Based on https://github.com/gaearon/redux-devtools/pull/241 /* eslint-disable no-param-reassign */ -export default function commitExcessActions(liftedState, n = 1) { +import { State } from '../reducers/instances'; + +export default function commitExcessActions(liftedState: State, n = 1) { // Auto-commits n-number of excess actions. let excess = n; let idsToDelete = liftedState.stagedActionIds.slice(1, excess + 1); diff --git a/packages/redux-devtools-core/src/app/utils/getMonitor.js b/packages/redux-devtools-core/src/app/utils/getMonitor.tsx similarity index 90% rename from packages/redux-devtools-core/src/app/utils/getMonitor.js rename to packages/redux-devtools-core/src/app/utils/getMonitor.tsx index 32a9ea5c..55379e1d 100644 --- a/packages/redux-devtools-core/src/app/utils/getMonitor.js +++ b/packages/redux-devtools-core/src/app/utils/getMonitor.tsx @@ -9,7 +9,7 @@ export const monitors = [ { value: 'ChartMonitor', name: 'Chart' }, ]; -export default function getMonitor({ monitor }) { +export default function getMonitor({ monitor }: { monitor: string }) { switch (monitor) { case 'LogMonitor': return ( diff --git a/packages/redux-devtools-core/src/app/utils/monitorActions.js b/packages/redux-devtools-core/src/app/utils/monitorActions.ts similarity index 78% rename from packages/redux-devtools-core/src/app/utils/monitorActions.js rename to packages/redux-devtools-core/src/app/utils/monitorActions.ts index 4cc58517..1e5fc604 100644 --- a/packages/redux-devtools-core/src/app/utils/monitorActions.js +++ b/packages/redux-devtools-core/src/app/utils/monitorActions.ts @@ -2,8 +2,12 @@ import difference from 'lodash/difference'; import omit from 'lodash/omit'; import stringifyJSON from './stringifyJSON'; import { SET_STATE } from '../constants/actionTypes'; +import { InstancesState, State } from '../reducers/instances'; +import { Dispatch, MiddlewareAPI } from 'redux'; +import { DispatchAction, StoreAction } from '../actions'; +import { StoreState } from '../reducers'; -export function sweep(state) { +export function sweep(state: State): State { return { ...state, actionsById: omit(state.actionsById, state.skippedActionIds), @@ -17,12 +21,12 @@ export function sweep(state) { } export function nonReduxDispatch( - store, - message, - instanceId, - action, - initialState, - preInstances + store: MiddlewareAPI, StoreState>, + message: string, + instanceId: string, + action: DispatchAction, + initialState: string | undefined, + preInstances: InstancesState ) { const instances = preInstances || store.getState().instances; const state = instances.states[instanceId]; diff --git a/packages/redux-devtools-core/src/app/utils/parseJSON.js b/packages/redux-devtools-core/src/app/utils/parseJSON.ts similarity index 70% rename from packages/redux-devtools-core/src/app/utils/parseJSON.js rename to packages/redux-devtools-core/src/app/utils/parseJSON.ts index 63370feb..3288fc20 100644 --- a/packages/redux-devtools-core/src/app/utils/parseJSON.js +++ b/packages/redux-devtools-core/src/app/utils/parseJSON.ts @@ -1,17 +1,17 @@ import jsan from 'jsan'; import { DATA_TYPE_KEY, DATA_REF_KEY } from '../constants/dataTypes'; -export function reviver(key, value) { +export function reviver(key: string, value: unknown) { if ( typeof value === 'object' && value !== null && '__serializedType__' in value && - typeof value.data === 'object' + typeof (value as any).data === 'object' ) { - const data = value.data; - data[DATA_TYPE_KEY] = value.__serializedType__; + const data = (value as any).data; + data[DATA_TYPE_KEY] = (value as any).__serializedType__; if ('__serializedRef__' in value) - data[DATA_REF_KEY] = value.__serializedRef__; + data[DATA_REF_KEY] = (value as any).__serializedRef__; /* if (Array.isArray(data)) { data.__serializedType__ = value.__serializedType__; @@ -26,7 +26,10 @@ export function reviver(key, value) { return value; } -export default function parseJSON(data, serialize) { +export default function parseJSON( + data: string | undefined, + serialize?: boolean +) { if (typeof data !== 'string') return data; try { return serialize ? jsan.parse(data, reviver) : jsan.parse(data); diff --git a/packages/redux-devtools-core/src/app/utils/stringifyJSON.js b/packages/redux-devtools-core/src/app/utils/stringifyJSON.js deleted file mode 100644 index d3191612..00000000 --- a/packages/redux-devtools-core/src/app/utils/stringifyJSON.js +++ /dev/null @@ -1,20 +0,0 @@ -import jsan from 'jsan'; -import { DATA_TYPE_KEY, DATA_REF_KEY } from '../constants/dataTypes'; - -function replacer(key, value) { - if (typeof value === 'object' && value !== null && DATA_TYPE_KEY in value) { - const __serializedType__ = value[DATA_TYPE_KEY]; - const clone = { ...value }; - delete clone[DATA_TYPE_KEY]; // eslint-disable-line no-param-reassign - const r = { data: clone, __serializedType__ }; - if (DATA_REF_KEY in value) r.__serializedRef__ = clone[DATA_REF_KEY]; - return r; - } - return value; -} - -export default function stringifyJSON(data, serialize) { - return serialize - ? jsan.stringify(data, replacer, null, true) - : jsan.stringify(data); -} diff --git a/packages/redux-devtools-core/src/app/utils/stringifyJSON.ts b/packages/redux-devtools-core/src/app/utils/stringifyJSON.ts new file mode 100644 index 00000000..403e621e --- /dev/null +++ b/packages/redux-devtools-core/src/app/utils/stringifyJSON.ts @@ -0,0 +1,24 @@ +import jsan from 'jsan'; +import { DATA_TYPE_KEY, DATA_REF_KEY } from '../constants/dataTypes'; + +function replacer(key: string, value: unknown) { + if (typeof value === 'object' && value !== null && DATA_TYPE_KEY in value) { + const __serializedType__ = (value as any)[DATA_TYPE_KEY]; + const clone = { ...value }; + delete (clone as any)[DATA_TYPE_KEY]; // eslint-disable-line no-param-reassign + const r = { data: clone, __serializedType__ }; + if (DATA_REF_KEY in value) + (r as any).__serializedRef__ = (clone as any)[DATA_REF_KEY]; + return r; + } + return value; +} + +export default function stringifyJSON( + data: unknown, + serialize: boolean | undefined +) { + return serialize + ? jsan.stringify(data, replacer, (null as unknown) as undefined, true) + : jsan.stringify(data); +} diff --git a/packages/redux-devtools-core/src/app/utils/updateState.js b/packages/redux-devtools-core/src/app/utils/updateState.ts similarity index 66% rename from packages/redux-devtools-core/src/app/utils/updateState.js rename to packages/redux-devtools-core/src/app/utils/updateState.ts index 7157da99..eb2ba394 100644 --- a/packages/redux-devtools-core/src/app/utils/updateState.js +++ b/packages/redux-devtools-core/src/app/utils/updateState.ts @@ -1,12 +1,17 @@ import commitExcessActions from './commitExcessActions'; +import { State } from '../reducers/instances'; +import { Action } from 'redux'; +import { PerformAction } from 'redux-devtools-instrument'; export function recompute( - previousLiftedState, - storeState, - action, + previousLiftedState: State, + storeState: State, + action: + | PerformAction> + | { action: Action; timestamp?: number; stack?: string }, nextActionId = 1, - maxAge, - isExcess + maxAge?: number, + isExcess?: boolean ) { const actionId = nextActionId - 1; const liftedState = { ...previousLiftedState }; @@ -19,8 +24,10 @@ export function recompute( } liftedState.stagedActionIds = [...liftedState.stagedActionIds, actionId]; liftedState.actionsById = { ...liftedState.actionsById }; - if (action.type === 'PERFORM_ACTION') { - liftedState.actionsById[actionId] = action; + if ((action as PerformAction>).type === 'PERFORM_ACTION') { + liftedState.actionsById[actionId] = action as PerformAction< + Action + >; } else { liftedState.actionsById[actionId] = { action: action.action || action, diff --git a/packages/redux-devtools-core/src/index.js b/packages/redux-devtools-core/src/index.js deleted file mode 100644 index 29d541ff..00000000 --- a/packages/redux-devtools-core/src/index.js +++ /dev/null @@ -1,8 +0,0 @@ -function injectedScript() { - /* eslint-disable-next-line no-console */ - console.error( - "Not implemented yet. WIP. If you're looking for utils, import `redux-devtools-core/lib/utils`." - ); -} - -export default injectedScript; diff --git a/packages/redux-devtools-core/index.js b/packages/redux-devtools-core/src/index.tsx similarity index 94% rename from packages/redux-devtools-core/index.js rename to packages/redux-devtools-core/src/index.tsx index bd23dae6..c40f8b2a 100644 --- a/packages/redux-devtools-core/index.js +++ b/packages/redux-devtools-core/src/index.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { render } from 'react-dom'; -import App from './src/app'; +import App from './app'; render(, document.getElementById('root')); diff --git a/packages/redux-devtools-core/src/utils/catchErrors.js b/packages/redux-devtools-core/src/utils/catchErrors.js deleted file mode 100644 index b6d42219..00000000 --- a/packages/redux-devtools-core/src/utils/catchErrors.js +++ /dev/null @@ -1,42 +0,0 @@ -const ERROR = '@@redux-devtools/ERROR'; - -export default function catchErrors(sendError) { - if (typeof window === 'object' && typeof window.onerror === 'object') { - window.onerror = function (message, url, lineNo, columnNo, error) { - const errorAction = { type: ERROR, message, url, lineNo, columnNo }; - if (error && error.stack) errorAction.stack = error.stack; - sendError(errorAction); - return false; - }; - } else if (typeof global !== 'undefined' && global.ErrorUtils) { - global.ErrorUtils.setGlobalHandler((error, isFatal) => { - sendError({ type: ERROR, error, isFatal }); - }); - } - - /* eslint-disable no-console */ - if ( - typeof console === 'object' && - typeof console.error === 'function' && - !console.beforeRemotedev - ) { - console.beforeRemotedev = console.error.bind(console); - console.error = function () { - let errorAction = { type: ERROR }; - const error = arguments[0]; - errorAction.message = error.message ? error.message : error; - if (error.sourceURL) { - errorAction = { - ...errorAction, - sourceURL: error.sourceURL, - line: error.line, - column: error.column, - }; - } - if (error.stack) errorAction.stack = error.stack; - sendError(errorAction); - console.beforeRemotedev.apply(null, arguments); - }; - } - /* eslint-enable no-console */ -} diff --git a/packages/redux-devtools-core/src/utils/catchErrors.ts b/packages/redux-devtools-core/src/utils/catchErrors.ts new file mode 100644 index 00000000..a04d2148 --- /dev/null +++ b/packages/redux-devtools-core/src/utils/catchErrors.ts @@ -0,0 +1,68 @@ +const ERROR = '@@redux-devtools/ERROR'; + +interface ErrorAction { + type: typeof ERROR; + message?: Event | string; + url?: string | undefined; + lineNo?: number | undefined; + columnNo?: number | undefined; + stack?: string; + error?: Error; + isFatal?: boolean; + sourceURL?: string; + line?: number; + column?: number; +} + +export default function catchErrors( + sendError: (errorAction: ErrorAction) => void +) { + if (typeof window === 'object' && typeof window.onerror === 'object') { + window.onerror = function (message, url, lineNo, columnNo, error) { + const errorAction: ErrorAction = { + type: ERROR, + message, + url, + lineNo, + columnNo, + }; + if (error && error.stack) errorAction.stack = error.stack; + sendError(errorAction); + return false; + }; + } else if (typeof global !== 'undefined' && (global as any).ErrorUtils) { + (global as any).ErrorUtils.setGlobalHandler( + (error: Error, isFatal: boolean) => { + sendError({ type: ERROR, error, isFatal }); + } + ); + } + + /* eslint-disable no-console */ + if ( + typeof console === 'object' && + typeof console.error === 'function' && + !(console as any).beforeRemotedev + ) { + (console as any).beforeRemotedev = console.error.bind(console); + console.error = function () { + let errorAction: ErrorAction = { type: ERROR }; + // eslint-disable-next-line prefer-rest-params + const error = arguments[0]; + errorAction.message = error.message ? error.message : error; + if (error.sourceURL) { + errorAction = { + ...errorAction, + sourceURL: error.sourceURL, + line: error.line, + column: error.column, + }; + } + if (error.stack) errorAction.stack = error.stack; + sendError(errorAction); + // eslint-disable-next-line prefer-rest-params + (console as any).beforeRemotedev.apply(null, arguments); + }; + } + /* eslint-enable no-console */ +} diff --git a/packages/redux-devtools-core/src/utils/filters.js b/packages/redux-devtools-core/src/utils/filters.ts similarity index 55% rename from packages/redux-devtools-core/src/utils/filters.js rename to packages/redux-devtools-core/src/utils/filters.ts index d6607f6b..9c9644b1 100644 --- a/packages/redux-devtools-core/src/utils/filters.js +++ b/packages/redux-devtools-core/src/utils/filters.ts @@ -1,4 +1,7 @@ import mapValues from 'lodash/mapValues'; +import { PerformAction } from 'redux-devtools-instrument'; +import { Action } from 'redux'; +import { State } from '../app/reducers/instances'; export const FilterState = { DO_NOT_FILTER: 'DO_NOT_FILTER', @@ -6,19 +9,25 @@ export const FilterState = { WHITELIST_SPECIFIC: 'WHITELIST_SPECIFIC', }; -export function arrToRegex(v) { +export function arrToRegex(v: string | string[]) { return typeof v === 'string' ? v : v.join('|'); } -function filterActions(actionsById, actionsFilter) { +function filterActions( + actionsById: { [actionId: number]: PerformAction> }, + actionsFilter: (action: Action, id: number) => Action +) { if (!actionsFilter) return actionsById; - return mapValues(actionsById, (action, id) => ({ + return mapValues(actionsById, (action, id: number) => ({ ...action, action: actionsFilter(action.action, id), })); } -function filterStates(computedStates, statesFilter) { +function filterStates( + computedStates: { state: unknown; error?: string | undefined }[], + statesFilter: (state: unknown, actionId: number) => unknown +) { if (!statesFilter) return computedStates; return computedStates.map((state, idx) => ({ ...state, @@ -26,7 +35,17 @@ function filterStates(computedStates, statesFilter) { })); } -export function getLocalFilter(config) { +interface Config { + actionsBlacklist?: string[]; + actionsWhitelist?: string[]; +} + +interface LocalFilter { + whitelist?: string; + blacklist?: string; +} + +export function getLocalFilter(config: Config): LocalFilter | undefined { if (config.actionsBlacklist || config.actionsWhitelist) { return { whitelist: config.actionsWhitelist && config.actionsWhitelist.join('|'), @@ -36,33 +55,53 @@ export function getLocalFilter(config) { return undefined; } +interface DevToolsOptions { + filter?: + | typeof FilterState.DO_NOT_FILTER + | typeof FilterState.BLACKLIST_SPECIFIC + | typeof FilterState.WHITELIST_SPECIFIC; + whitelist?: string; + blacklist?: string; +} function getDevToolsOptions() { - return (typeof window !== 'undefined' && window.devToolsOptions) || {}; + return ( + (typeof window !== 'undefined' && + (window as { devToolsOptions?: DevToolsOptions }).devToolsOptions) || + {} + ); } -export function isFiltered(action, localFilter) { - const { type } = action.action || action; +export function isFiltered( + action: PerformAction> | Action, + localFilter?: LocalFilter +) { + const { type } = (action as PerformAction>).action || action; const opts = getDevToolsOptions(); if ( (!localFilter && opts.filter && opts.filter === FilterState.DO_NOT_FILTER) || - (type && typeof type.match !== 'function') + (type && typeof (type as string).match !== 'function') ) return false; const { whitelist, blacklist } = localFilter || opts; return ( - (whitelist && !type.match(whitelist)) || - (blacklist && type.match(blacklist)) + // eslint-disable-next-line @typescript-eslint/prefer-regexp-exec + (whitelist && !(type as string).match(whitelist)) || + // eslint-disable-next-line @typescript-eslint/prefer-regexp-exec + (blacklist && (type as string).match(blacklist)) ); } -export function filterStagedActions(state, filters) { +export function filterStagedActions(state: State, filters: LocalFilter) { if (!filters) return state; - const filteredStagedActionIds = []; - const filteredComputedStates = []; + const filteredStagedActionIds: number[] = []; + const filteredComputedStates: { + state: unknown; + error?: string | undefined; + }[] = []; state.stagedActionIds.forEach((id, idx) => { if (!isFiltered(state.actionsById[id], filters)) { @@ -79,13 +118,13 @@ export function filterStagedActions(state, filters) { } export function filterState( - state, - type, - localFilter, - stateSanitizer, - actionSanitizer, - nextActionId, - predicate + state: State, + type: string, + localFilter: LocalFilter, + stateSanitizer: (state: unknown, actionId: number) => unknown, + actionSanitizer: (action: Action, id: number) => Action, + nextActionId: number, + predicate: (currState: unknown, currAction: Action) => boolean ) { if (type === 'ACTION') return !stateSanitizer ? state : stateSanitizer(state, nextActionId - 1); @@ -97,9 +136,14 @@ export function filterState( localFilter || (filter && filter !== FilterState.DO_NOT_FILTER) ) { - const filteredStagedActionIds = []; - const filteredComputedStates = []; - const sanitizedActionsById = actionSanitizer && {}; + const filteredStagedActionIds: number[] = []; + const filteredComputedStates: { + state: unknown; + error?: string | undefined; + }[] = []; + const sanitizedActionsById: { + [id: number]: PerformAction>; + } = actionSanitizer && {}; const { actionsById } = state; const { computedStates } = state; diff --git a/packages/redux-devtools-core/src/utils/get-params.ts b/packages/redux-devtools-core/src/utils/get-params.ts new file mode 100644 index 00000000..417ec500 --- /dev/null +++ b/packages/redux-devtools-core/src/utils/get-params.ts @@ -0,0 +1,4 @@ +declare module 'get-params' { + function getParams(func: (...args: any[]) => unknown): string[]; + export default getParams; +} diff --git a/packages/redux-devtools-core/src/utils/importState.js b/packages/redux-devtools-core/src/utils/importState.js deleted file mode 100644 index 11cf3222..00000000 --- a/packages/redux-devtools-core/src/utils/importState.js +++ /dev/null @@ -1,69 +0,0 @@ -import mapValues from 'lodash/mapValues'; -import jsan from 'jsan'; -import seralizeImmutable from 'remotedev-serialize/immutable/serialize'; - -function deprecate(param) { - // eslint-disable-next-line no-console - console.warn( - `\`${param}\` parameter for Redux DevTools Extension is deprecated. Use \`serialize\` parameter instead:` + - ' https://github.com/zalmoxisus/redux-devtools-extension/releases/tag/v2.12.1' - ); -} - -export default function importState( - state, - { deserializeState, deserializeAction, serialize } -) { - if (!state) return undefined; - let parse = jsan.parse; - if (serialize) { - if (serialize.immutable) { - parse = (v) => - jsan.parse( - v, - seralizeImmutable(serialize.immutable, serialize.refs).reviver - ); - } else if (serialize.reviver) { - parse = (v) => jsan.parse(v, serialize.reviver); - } - } - - let preloadedState; - let nextLiftedState = parse(state); - if (nextLiftedState.payload) { - if (nextLiftedState.preloadedState) - preloadedState = parse(nextLiftedState.preloadedState); - nextLiftedState = parse(nextLiftedState.payload); - } - if (deserializeState) { - deprecate('deserializeState'); - if (typeof nextLiftedState.computedStates !== 'undefined') { - nextLiftedState.computedStates = nextLiftedState.computedStates.map( - (computedState) => ({ - ...computedState, - state: deserializeState(computedState.state), - }) - ); - } - if (typeof nextLiftedState.committedState !== 'undefined') { - nextLiftedState.committedState = deserializeState( - nextLiftedState.committedState - ); - } - if (typeof preloadedState !== 'undefined') { - preloadedState = deserializeState(preloadedState); - } - } - if (deserializeAction) { - deprecate('deserializeAction'); - nextLiftedState.actionsById = mapValues( - nextLiftedState.actionsById, - (liftedAction) => ({ - ...liftedAction, - action: deserializeAction(liftedAction.action), - }) - ); - } - - return { nextLiftedState, preloadedState }; -} diff --git a/packages/redux-devtools-core/src/utils/importState.ts b/packages/redux-devtools-core/src/utils/importState.ts new file mode 100644 index 00000000..1afe997f --- /dev/null +++ b/packages/redux-devtools-core/src/utils/importState.ts @@ -0,0 +1,105 @@ +import mapValues from 'lodash/mapValues'; +import jsan from 'jsan'; +import { immutableSerialize } from 'redux-devtools-serialize'; +import { Action } from 'redux'; +import Immutable from 'immutable'; +import { State } from '../app/reducers/instances'; + +function deprecate(param: string) { + // eslint-disable-next-line no-console + console.warn( + `\`${param}\` parameter for Redux DevTools Extension is deprecated. Use \`serialize\` parameter instead:` + + ' https://github.com/zalmoxisus/redux-devtools-extension/releases/tag/v2.12.1' + ); +} + +export default function importState( + state: string, + { + deserializeState, + deserializeAction, + serialize, + }: { + deserializeState?: (state: string) => unknown; + deserializeAction?: (action: string) => Action; + serialize?: { + immutable?: typeof Immutable; + refs?: (new (data: any) => unknown)[] | null; + reviver?: (key: string, value: unknown) => unknown; + }; + } +) { + if (!state) return undefined; + let parse = jsan.parse; + if (serialize) { + if (serialize.immutable) { + parse = (v) => + jsan.parse( + v, + immutableSerialize(serialize.immutable!, serialize.refs).reviver + ); + } else if (serialize.reviver) { + parse = (v) => jsan.parse(v, serialize.reviver); + } + } + + let preloadedState: State | undefined; + let nextLiftedState: State = parse(state) as State; + if ( + ((nextLiftedState as unknown) as { + payload?: string; + preloadedState?: string; + }).payload + ) { + if ( + ((nextLiftedState as unknown) as { + payload: string; + preloadedState?: string; + }).preloadedState + ) + preloadedState = parse( + ((nextLiftedState as unknown) as { + payload: string; + preloadedState: string; + }).preloadedState + ) as State; + nextLiftedState = parse( + ((nextLiftedState as unknown) as { + payload: string; + }).payload + ) as State; + } + if (deserializeState) { + deprecate('deserializeState'); + if (typeof nextLiftedState.computedStates !== 'undefined') { + nextLiftedState.computedStates = nextLiftedState.computedStates.map( + (computedState) => ({ + ...computedState, + state: deserializeState(computedState.state as string), + }) + ); + } + if (typeof nextLiftedState.committedState !== 'undefined') { + nextLiftedState.committedState = deserializeState( + nextLiftedState.committedState as string + ); + } + if (typeof preloadedState !== 'undefined') { + preloadedState = deserializeState( + (preloadedState as unknown) as string + ) as State; + } + } + if (deserializeAction) { + deprecate('deserializeAction'); + nextLiftedState.actionsById = mapValues( + nextLiftedState.actionsById, + (liftedAction) => ({ + ...liftedAction, + action: deserializeAction((liftedAction.action as unknown) as string), + }) + ); + } + + return { nextLiftedState, preloadedState }; +} diff --git a/packages/redux-devtools-core/src/utils/index.js b/packages/redux-devtools-core/src/utils/index.ts similarity index 55% rename from packages/redux-devtools-core/src/utils/index.js rename to packages/redux-devtools-core/src/utils/index.ts index 45bd643c..c343b293 100644 --- a/packages/redux-devtools-core/src/utils/index.js +++ b/packages/redux-devtools-core/src/utils/index.ts @@ -1,14 +1,24 @@ import getParams from 'get-params'; import jsan from 'jsan'; import { nanoid } from 'nanoid/non-secure'; -import seralizeImmutable from 'remotedev-serialize/immutable/serialize'; +import { immutableSerialize } from 'redux-devtools-serialize'; +import Immutable from 'immutable'; +import { Action } from 'redux'; -export function generateId(id) { +export function generateId(id: string | undefined) { return id || nanoid(7); } -function flatTree(obj, namespace = '') { - let functions = []; +// eslint-disable-next-line @typescript-eslint/ban-types +function flatTree( + obj: { [key: string]: (...args: any[]) => unknown }, + namespace = '' +) { + let functions: { + name: string; + func: (...args: any[]) => unknown; + args: string[]; + }[] = []; Object.keys(obj).forEach((key) => { const prop = obj[key]; if (typeof prop === 'function') { @@ -24,18 +34,23 @@ function flatTree(obj, namespace = '') { return functions; } -export function getMethods(obj) { +export function getMethods(obj: unknown) { if (typeof obj !== 'object') return undefined; - let functions; - let m; - if (obj.__proto__) m = obj.__proto__.__proto__; - if (!m) m = obj; + let functions: + | { + name: string; + args: string[]; + }[] + | undefined; + let m: { [key: string]: (...args: any[]) => unknown } | undefined; + if ((obj as any).__proto__) m = (obj as any).__proto__.__proto__; + if (!m) m = obj as any; Object.getOwnPropertyNames(m).forEach((key) => { const propDescriptor = Object.getOwnPropertyDescriptor(m, key); if (!propDescriptor || 'get' in propDescriptor || 'set' in propDescriptor) return; - const prop = m[key]; + const prop = m![key]; if (typeof prop === 'function' && key !== 'constructor') { if (!functions) functions = []; functions.push({ @@ -47,15 +62,17 @@ export function getMethods(obj) { return functions; } -export function getActionsArray(actionCreators) { +export function getActionsArray(actionCreators: { + [key: string]: (...args: any[]) => unknown; +}) { if (Array.isArray(actionCreators)) return actionCreators; return flatTree(actionCreators); } -/* eslint-disable no-new-func */ -const interpretArg = (arg) => new Function('return ' + arg)(); +// eslint-disable-next-line @typescript-eslint/no-implied-eval +const interpretArg = (arg: string) => new Function('return ' + arg)(); -function evalArgs(inArgs, restArgs) { +function evalArgs(inArgs: string[], restArgs: string) { const args = inArgs.map(interpretArg); if (!restArgs) return args; const rest = interpretArg(restArgs); @@ -63,8 +80,14 @@ function evalArgs(inArgs, restArgs) { throw new Error('rest must be an array'); } -export function evalAction(action, actionCreators) { +export function evalAction( + action: string | { args: string[]; rest: string; selected: string }, + actionCreators: { + [selected: string]: { func: (...args: any[]) => Action }; + } +) { if (typeof action === 'string') { + // eslint-disable-next-line @typescript-eslint/no-implied-eval return new Function('return ' + action)(); } @@ -73,12 +96,17 @@ export function evalAction(action, actionCreators) { return actionCreator(...args); } -export function evalMethod(action, obj) { +export function evalMethod( + action: string | { args: string[]; rest: string; name: string }, + obj: unknown +) { if (typeof action === 'string') { + // eslint-disable-next-line @typescript-eslint/no-implied-eval return new Function('return ' + action).call(obj); } const args = evalArgs(action.args, action.rest); + // eslint-disable-next-line @typescript-eslint/no-implied-eval return new Function('args', `return this.${action.name}(args)`).apply( obj, args @@ -86,7 +114,7 @@ export function evalMethod(action, obj) { } /* eslint-enable */ -function tryCatchStringify(obj) { +function tryCatchStringify(obj: unknown) { try { return JSON.stringify(obj); } catch (err) { @@ -94,11 +122,26 @@ function tryCatchStringify(obj) { if (process.env.NODE_ENV !== 'production') console.log('Failed to stringify', err); /* eslint-enable no-console */ - return jsan.stringify(obj, null, null, { circular: '[CIRCULAR]' }); + return jsan.stringify( + obj, + (null as unknown) as undefined, + (null as unknown) as undefined, + ({ + circular: '[CIRCULAR]', + } as unknown) as boolean + ); } } -export function stringify(obj, serialize) { +export function stringify( + obj: unknown, + serialize?: + | { + replacer?: (key: string, value: unknown) => unknown; + options?: unknown | boolean; + } + | true +) { if (typeof serialize === 'undefined') { return tryCatchStringify(obj); } @@ -106,23 +149,44 @@ export function stringify(obj, serialize) { return jsan.stringify( obj, function (key, value) { - if (value && typeof value.toJS === 'function') return value.toJS(); + if (value && typeof (value as any).toJS === 'function') + return (value as any).toJS(); return value; }, - null, + (null as unknown) as undefined, true ); } - return jsan.stringify(obj, serialize.replacer, null, serialize.options); + return jsan.stringify( + obj, + serialize.replacer, + (null as unknown) as undefined, + serialize.options as boolean + ); } -export function getSeralizeParameter(config, param) { +export function getSeralizeParameter( + config: { + serialize?: { + immutable?: typeof Immutable; + refs?: (new (data: any) => unknown)[] | null; + replacer?: (key: string, value: unknown) => unknown; + options?: unknown | boolean; + }; + }, + param: string +): + | { + replacer?: (key: string, value: unknown) => unknown; + options: unknown | boolean; + } + | undefined { const serialize = config.serialize; if (serialize) { if (serialize === true) return { options: true }; if (serialize.immutable) { return { - replacer: seralizeImmutable(serialize.immutable, serialize.refs) + replacer: immutableSerialize(serialize.immutable, serialize.refs) .replacer, options: serialize.options || true, }; @@ -131,7 +195,12 @@ export function getSeralizeParameter(config, param) { return { replacer: serialize.replacer, options: serialize.options || true }; } - const value = config[param]; + const value = (config as { + [param: string]: { + replacer?: (key: string, value: unknown) => unknown; + options: unknown | boolean; + }; + })[param]; if (typeof value === 'undefined') return undefined; // eslint-disable-next-line no-console console.warn( @@ -139,12 +208,15 @@ export function getSeralizeParameter(config, param) { ' https://github.com/zalmoxisus/redux-devtools-extension/releases/tag/v2.12.1' ); - if (typeof serializeState === 'boolean') return { options: value }; - if (typeof serializeState === 'function') return { replacer: value }; return value; } -export function getStackTrace(config, toExcludeFromTrace) { +export function getStackTrace( + // eslint-disable-next-line @typescript-eslint/ban-types + config: { trace?: () => {}; traceLimit: number }, + // eslint-disable-next-line @typescript-eslint/ban-types + toExcludeFromTrace?: Function | undefined +) { if (!config.trace) return undefined; if (typeof config.trace === 'function') return config.trace(); @@ -169,7 +241,7 @@ export function getStackTrace(config, toExcludeFromTrace) { typeof Error.stackTraceLimit !== 'number' || Error.stackTraceLimit > traceLimit ) { - const frames = stack.split('\n'); + const frames = stack!.split('\n'); if (frames.length > traceLimit) { stack = frames .slice(0, traceLimit + extraFrames + (frames[0] === 'Error' ? 1 : 0)) diff --git a/packages/redux-devtools-core/test/__mocks__/styleMock.js b/packages/redux-devtools-core/test/__mocks__/styleMock.js deleted file mode 100644 index f053ebf7..00000000 --- a/packages/redux-devtools-core/test/__mocks__/styleMock.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = {}; diff --git a/packages/redux-devtools-core/test/app.spec.js b/packages/redux-devtools-core/test/app.spec.tsx similarity index 92% rename from packages/redux-devtools-core/test/app.spec.js rename to packages/redux-devtools-core/test/app.spec.tsx index 71b42159..d7ce85ee 100644 --- a/packages/redux-devtools-core/test/app.spec.js +++ b/packages/redux-devtools-core/test/app.spec.tsx @@ -1,7 +1,7 @@ -import React from 'react'; +import React, { Component } from 'react'; import { Provider } from 'react-redux'; import { createStore, applyMiddleware } from 'redux'; -import { mount } from 'enzyme'; +import { mount, ReactWrapper } from 'enzyme'; // import { mountToJson } from 'enzyme-to-json'; import App from '../src/app/containers/App'; import api from '../src/app/middlewares/api'; @@ -10,7 +10,7 @@ import rootReducer from '../src/app/reducers'; import { DATA_TYPE_KEY } from '../src/app/constants/dataTypes'; import stringifyJSON from '../src/app/utils/stringifyJSON'; -let wrapper; +let wrapper: ReactWrapper; const store = createStore(rootReducer, applyMiddleware(exportState, api)); diff --git a/packages/redux-devtools-core/test/setup.js b/packages/redux-devtools-core/test/setup.ts similarity index 100% rename from packages/redux-devtools-core/test/setup.js rename to packages/redux-devtools-core/test/setup.ts diff --git a/packages/redux-devtools-core/test/tsconfig.json b/packages/redux-devtools-core/test/tsconfig.json new file mode 100644 index 00000000..ca19def4 --- /dev/null +++ b/packages/redux-devtools-core/test/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../../tsconfig.react.base.json", + "include": ["../src", "."] +} diff --git a/packages/redux-devtools-core/tsconfig.json b/packages/redux-devtools-core/tsconfig.json new file mode 100644 index 00000000..7b7d1492 --- /dev/null +++ b/packages/redux-devtools-core/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.react.base.json", + "compilerOptions": { + "outDir": "lib" + }, + "include": ["src"] +} diff --git a/packages/redux-devtools-core/tsconfig.webpack.json b/packages/redux-devtools-core/tsconfig.webpack.json new file mode 100644 index 00000000..6ab34185 --- /dev/null +++ b/packages/redux-devtools-core/tsconfig.webpack.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["webpack.config.ts", "webpack.config.umd.ts"] +} diff --git a/packages/redux-devtools-core/webpack.config.js b/packages/redux-devtools-core/webpack.config.ts similarity index 72% rename from packages/redux-devtools-core/webpack.config.js rename to packages/redux-devtools-core/webpack.config.ts index ed75e5f7..75d1c5ef 100644 --- a/packages/redux-devtools-core/webpack.config.js +++ b/packages/redux-devtools-core/webpack.config.ts @@ -1,14 +1,15 @@ -const path = require('path'); -const webpack = require('webpack'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); +import * as path from 'path'; +import * as webpack from 'webpack'; +import HtmlWebpackPlugin from 'html-webpack-plugin'; +import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin'; -module.exports = (env = {}) => ({ +module.exports = (env: { development?: boolean; platform?: string } = {}) => ({ mode: env.development ? 'development' : 'production', entry: { - app: './index.js', + app: './src/index', }, output: { - path: path.resolve(__dirname, 'build/' + env.platform), + path: path.resolve(__dirname, `build/${env.platform as string}`), publicPath: '', filename: 'js/[name].js', sourceMapFilename: 'js/[name].map', @@ -16,7 +17,7 @@ module.exports = (env = {}) => ({ module: { rules: [ { - test: /\.js$/, + test: /\.(js|ts)x?$/, loader: 'babel-loader', exclude: /node_modules/, }, @@ -44,6 +45,9 @@ module.exports = (env = {}) => ({ }, ], }, + resolve: { + extensions: ['.js', '.jsx', '.ts', '.tsx'], + }, plugins: [ new webpack.DefinePlugin({ 'process.env': { @@ -56,6 +60,11 @@ module.exports = (env = {}) => ({ new HtmlWebpackPlugin({ template: 'assets/index.html', }), + new ForkTsCheckerWebpackPlugin({ + typescript: { + configFile: 'tsconfig.json', + }, + }), ], optimization: { minimize: false, diff --git a/packages/redux-devtools-core/webpack.config.umd.js b/packages/redux-devtools-core/webpack.config.umd.ts similarity index 74% rename from packages/redux-devtools-core/webpack.config.umd.js rename to packages/redux-devtools-core/webpack.config.umd.ts index 8e31df45..99abaf62 100644 --- a/packages/redux-devtools-core/webpack.config.umd.js +++ b/packages/redux-devtools-core/webpack.config.umd.ts @@ -1,10 +1,11 @@ -const path = require('path'); -const webpack = require('webpack'); +import * as path from 'path'; +import * as webpack from 'webpack'; +import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin'; -module.exports = (env = {}) => ({ +module.exports = (env: { production?: boolean } = {}) => ({ mode: env.production ? 'production' : 'development', entry: { - app: ['./src/app/index.js'], + app: ['./src/app/index'], }, output: { library: 'ReduxDevTools', @@ -18,7 +19,7 @@ module.exports = (env = {}) => ({ module: { rules: [ { - test: /\.js$/, + test: /\.(js|ts)x?$/, loader: 'babel-loader', exclude: /node_modules/, }, @@ -41,6 +42,9 @@ module.exports = (env = {}) => ({ }, ], }, + resolve: { + extensions: ['.js', '.jsx', '.ts', '.tsx'], + }, plugins: [ new webpack.DefinePlugin({ 'process.env': { @@ -48,6 +52,11 @@ module.exports = (env = {}) => ({ PLATFORM: JSON.stringify('web'), }, }), + new ForkTsCheckerWebpackPlugin({ + typescript: { + configFile: 'tsconfig.json', + }, + }), ], externals: { react: { diff --git a/packages/redux-devtools-inspector-monitor/src/ActionPreview.tsx b/packages/redux-devtools-inspector-monitor/src/ActionPreview.tsx index 37c7d887..71b3f0c2 100644 --- a/packages/redux-devtools-inspector-monitor/src/ActionPreview.tsx +++ b/packages/redux-devtools-inspector-monitor/src/ActionPreview.tsx @@ -25,7 +25,7 @@ export interface TabComponentProps> { base16Theme: Base16Theme; invertTheme: boolean; isWideLayout: boolean; - dataTypeKey: string | undefined; + dataTypeKey: string | symbol | undefined; delta: Delta | null | undefined | false; action: A; nextState: S; @@ -67,7 +67,7 @@ interface Props> { actions: { [actionId: number]: PerformAction }; selectedActionId: number | null; startActionId: number | null; - dataTypeKey: string | undefined; + dataTypeKey: string | symbol | undefined; monitorState: DevtoolsInspectorState; updateMonitorState: (monitorState: Partial) => void; styling: StylingFunction; diff --git a/packages/redux-devtools-inspector-monitor/src/DevtoolsInspector.tsx b/packages/redux-devtools-inspector-monitor/src/DevtoolsInspector.tsx index 5800feab..a5714b20 100644 --- a/packages/redux-devtools-inspector-monitor/src/DevtoolsInspector.tsx +++ b/packages/redux-devtools-inspector-monitor/src/DevtoolsInspector.tsx @@ -128,7 +128,7 @@ function createThemeState>( return { base16Theme, styling }; } -interface ExternalProps> { +export interface ExternalProps> { dispatch: Dispatch< DevtoolsInspectorAction | LiftedAction >; @@ -142,7 +142,7 @@ interface ExternalProps> { hideMainButtons?: boolean; hideActionButtons?: boolean; invertTheme: boolean; - dataTypeKey?: string; + dataTypeKey?: string | symbol; tabs: Tab[] | ((tabs: Tab[]) => Tab[]); } @@ -169,7 +169,7 @@ export interface DevtoolsInspectorProps> hideMainButtons?: boolean; hideActionButtons?: boolean; invertTheme: boolean; - dataTypeKey?: string; + dataTypeKey?: string | symbol; tabs: Tab[] | ((tabs: Tab[]) => Tab[]); } diff --git a/packages/redux-devtools-inspector-monitor/src/redux.ts b/packages/redux-devtools-inspector-monitor/src/redux.ts index e7f14c87..93b2835d 100644 --- a/packages/redux-devtools-inspector-monitor/src/redux.ts +++ b/packages/redux-devtools-inspector-monitor/src/redux.ts @@ -4,7 +4,7 @@ import { DevtoolsInspectorProps } from './DevtoolsInspector'; const UPDATE_MONITOR_STATE = '@@redux-devtools-inspector-monitor/UPDATE_MONITOR_STATE'; -interface UpdateMonitorStateAction { +export interface UpdateMonitorStateAction { type: typeof UPDATE_MONITOR_STATE; monitorState: Partial; } diff --git a/packages/redux-devtools-inspector-monitor/src/tabs/JSONDiff.tsx b/packages/redux-devtools-inspector-monitor/src/tabs/JSONDiff.tsx index efcd8038..74213aaa 100644 --- a/packages/redux-devtools-inspector-monitor/src/tabs/JSONDiff.tsx +++ b/packages/redux-devtools-inspector-monitor/src/tabs/JSONDiff.tsx @@ -60,7 +60,7 @@ interface Props { expandable: boolean ) => React.ReactNode; isWideLayout: boolean; - dataTypeKey: string | undefined; + dataTypeKey: string | symbol | undefined; } interface State { diff --git a/packages/redux-devtools-inspector-monitor/src/tabs/getItemString.tsx b/packages/redux-devtools-inspector-monitor/src/tabs/getItemString.tsx index 85eb261a..515643ea 100644 --- a/packages/redux-devtools-inspector-monitor/src/tabs/getItemString.tsx +++ b/packages/redux-devtools-inspector-monitor/src/tabs/getItemString.tsx @@ -73,7 +73,7 @@ const getItemString = ( styling: StylingFunction, type: string, data: any, - dataTypeKey: string | undefined, + dataTypeKey: string | symbol | undefined, isWideLayout: boolean, isDiff?: boolean ) => ( diff --git a/packages/redux-devtools-serialize/src/immutable/index.ts b/packages/redux-devtools-serialize/src/immutable/index.ts index b8fd874b..08382573 100644 --- a/packages/redux-devtools-serialize/src/immutable/index.ts +++ b/packages/redux-devtools-serialize/src/immutable/index.ts @@ -35,3 +35,4 @@ export default function ( serialize: serialize, }; } +export { default as serialize } from './serialize'; diff --git a/packages/redux-devtools-serialize/src/index.ts b/packages/redux-devtools-serialize/src/index.ts index 600d22c0..c2f42ae8 100644 --- a/packages/redux-devtools-serialize/src/index.ts +++ b/packages/redux-devtools-serialize/src/index.ts @@ -1,5 +1,4 @@ -import immutable from './immutable'; - -module.exports = { - immutable: immutable, -}; +export { + default as immutable, + serialize as immutableSerialize, +} from './immutable'; diff --git a/yarn.lock b/yarn.lock index daf18650..80e9176d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3361,6 +3361,11 @@ resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a" integrity sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA== +"@types/async@*": + version "3.2.3" + resolved "https://registry.yarnpkg.com/@types/async/-/async-3.2.3.tgz#c56f5e0fc02f8b37196f79239cc857e789b97bb4" + integrity sha512-deXFjLZc1h6SOh3hicVgD+S2EAkhSBGX/vdlD4nTzCjjOFQ+bfNiXocQ21xJjFAUwqaCeyvOQMgrnbg4QEV63A== + "@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" @@ -3463,6 +3468,11 @@ dependencies: "@types/color-convert" "*" +"@types/component-emitter@*": + version "1.2.10" + resolved "https://registry.yarnpkg.com/@types/component-emitter/-/component-emitter-1.2.10.tgz#ef5b1589b9f16544642e473db5ea5639107ef3ea" + integrity sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg== + "@types/connect-history-api-fallback@*": version "1.3.3" resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.3.tgz#4772b79b8b53185f0f4c9deab09236baf76ee3b4" @@ -3545,6 +3555,11 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.45.tgz#e9387572998e5ecdac221950dab3e8c3b16af884" integrity sha512-jnqIUKDUqJbDIUxm0Uj7bnlMnRm1T/eZ9N+AVMqhPgzrba2GhGG5o/jCTwmdPK709nEZsGoMzXEDUjcXHa3W0g== +"@types/expirymanager@*": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@types/expirymanager/-/expirymanager-0.9.0.tgz#b2ca7610e517924e26b27221603cbe5f92c0e8fc" + integrity sha512-xulG8b5SiBhpRE1Arfx3ji428mfhwQdas6/i+1IJhTLkyFifJ4rF+vve522ds2ZTiBKCUv9WHNuVF/V9PJCa2Q== + "@types/express-serve-static-core@*", "@types/express-serve-static-core@4.17.9": version "4.17.9" resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.9.tgz#2d7b34dcfd25ec663c25c85d76608f8b249667f1" @@ -3576,6 +3591,11 @@ resolved "https://registry.yarnpkg.com/@types/filewriter/-/filewriter-0.0.28.tgz#c054e8af4d9dd75db4e63abc76f885168714d4b3" integrity sha1-wFTor02d11205jq8dviFFocU1LM= +"@types/fleximap@*": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@types/fleximap/-/fleximap-0.9.0.tgz#8f084b26bf7284800ee82d960df97324888f555d" + integrity sha512-7VsHgMM7l3jY+MXptDgzvROcEoikVgIxu+8d/qT0WijDl6RXdwAbAQYxu5sBCwUvlf0cEQwiDC4rOvkcm3h+hw== + "@types/fs-capacitor@*": version "2.0.0" resolved "https://registry.yarnpkg.com/@types/fs-capacitor/-/fs-capacitor-2.0.0.tgz#17113e25817f584f58100fb7a08eed288b81956e" @@ -3734,6 +3754,18 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.5.tgz#dcce4430e64b443ba8945f0290fb564ad5bac6dd" integrity sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ== +"@types/json-schema@^7.0.6": + version "7.0.6" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0" + integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw== + +"@types/jsonwebtoken@*": + version "8.5.0" + resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz#2531d5e300803aa63279b232c014acf780c981c5" + integrity sha512-9bVao7LvyorRGZCw0VmH/dr7Og+NdjYSsKAxB43OQoComFbBgsEpoR9JW6+qSq/ogwVBg8GI2MfAlk4SYI4OLg== + dependencies: + "@types/node" "*" + "@types/keygrip@*": version "1.0.2" resolved "https://registry.yarnpkg.com/@types/keygrip/-/keygrip-1.0.2.tgz#513abfd256d7ad0bf1ee1873606317b33b1b2a72" @@ -3827,6 +3859,13 @@ resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.0.tgz#69a23a3ad29caf0097f06eda59b361ee2f0639f6" integrity sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY= +"@types/ncom@*": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/ncom/-/ncom-1.0.0.tgz#8e33d06fc4914c941ba40ceca042081b947ba699" + integrity sha512-9fmYuP/lvEVfzY+5nZ61ewM/ub9mDINn88BBflTyHX6D7wH5b8oFR3GXrmYjelx79shuogHhHMOtXODDBatVPQ== + dependencies: + "@types/node" "*" + "@types/node-fetch@2.5.7", "@types/node-fetch@^2.5.4": version "2.5.7" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.7.tgz#20a2afffa882ab04d44ca786449a276f9f6bbf3c" @@ -4036,6 +4075,47 @@ dependencies: redux "^4.0.0" +"@types/sc-auth@*": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@types/sc-auth/-/sc-auth-5.0.0.tgz#b9bca82783419233ed938f59e37ae940bfdb454a" + integrity sha512-V+wuOweEJDrVCMduXmS7zc60O6HGyd5Xm3ClzEXKJfQdrSuhoqvhDjOwbtRZAXCjHll12lBXECb2sht5Glp/6A== + dependencies: + "@types/jsonwebtoken" "*" + +"@types/sc-broker-cluster@^6": + version "6.1.3" + resolved "https://registry.yarnpkg.com/@types/sc-broker-cluster/-/sc-broker-cluster-6.1.3.tgz#b6b0c3cedb635b1ee5098ebd1453e121c6a3d2cc" + integrity sha512-ttxBDnqq+Kcd3lMRQKW471sbv8KBXhJNaKHfFGrRRjWnSpSMa/zhhyAf/ew7/r8S7ZKuR4MFYmKYOwMXv5mm3g== + dependencies: + "@types/async" "*" + "@types/expirymanager" "*" + "@types/fleximap" "*" + "@types/sc-broker" "*" + "@types/sc-channel" "^1" + "@types/socketcluster" "*" + "@types/socketcluster-server" "^14" + +"@types/sc-broker@*": + version "8.0.1" + resolved "https://registry.yarnpkg.com/@types/sc-broker/-/sc-broker-8.0.1.tgz#7dcf741386ab08ca9ca39ea6eb9af7fbb77faff2" + integrity sha512-JacbIkcjKs3PIMyw8yuM3rrJf2kw39GMoT83tNyJfvhF0DBLU26OBsO4kxmi82SdnVUubeiSN4/whC4lhOpAYg== + dependencies: + "@types/expirymanager" "*" + "@types/fleximap" "*" + "@types/ncom" "*" + +"@types/sc-channel@^1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@types/sc-channel/-/sc-channel-1.2.1.tgz#82e7f50155699b751a7150b679960ca363bfa0b1" + integrity sha512-RYT2V1XlViii3CmPqlZQfv3ADaCzxGPhZfk6MWPfnv3z1dR9wCE/c9lehLtuCz0TYPJYPV48PoocvIdaPAMsUA== + dependencies: + "@types/component-emitter" "*" + +"@types/sc-errors@*": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@types/sc-errors/-/sc-errors-1.4.0.tgz#dba1309b695ee8aafc3f574dfedfe4f3c5153419" + integrity sha512-WfBEiw/SVja1ZvJRdt37dOJFxp2llV35n9cPiDCDsFRSvTTYlO4iMFg+NyeEhiWBk1O4bvmyYpAEYzJx1DbHHQ== + "@types/serve-static@*": version "1.13.5" resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.5.tgz#3d25d941a18415d3ab092def846e135a08bbcf53" @@ -4054,6 +4134,38 @@ resolved "https://registry.yarnpkg.com/@types/simple-element-resize-detector/-/simple-element-resize-detector-1.3.0.tgz#19b40d71fefa1876ac5d4ba585197ef438946353" integrity sha512-z89ForrCNg+4uwTHjwBCM9LjcsXYC/4O8u3tSi+82v2LCbfiYFpkjH/qQVkDewFBK6FUG7RRV7jw78EGs2maoQ== +"@types/socketcluster-client@^13.0.3": + version "13.0.4" + resolved "https://registry.yarnpkg.com/@types/socketcluster-client/-/socketcluster-client-13.0.4.tgz#945593f0a67d09d51b8fb39ea1f750fb6c19aea1" + integrity sha512-kJcm1V+iu09O7+zHY+zkdokNsvu2D965yqs8NhswgWxWU9ctk5f3IcbqMWnNqIe28aEFhvseJrlNM+NCjAEIwQ== + dependencies: + "@types/component-emitter" "*" + "@types/sc-auth" "*" + "@types/sc-channel" "^1" + "@types/sc-errors" "*" + "@types/socketcluster-server" "^14" + "@types/ws" "*" + +"@types/socketcluster-server@^14": + version "14.2.5" + resolved "https://registry.yarnpkg.com/@types/socketcluster-server/-/socketcluster-server-14.2.5.tgz#ed323b27e29de8a68dee851096d5fe2d164a8fd1" + integrity sha512-mDdLtv8R43mh6K4w/HmHEkZZMlCKN/B1Cm/seh6NSPBtVxbHhH0hN0KV4em4eY+2+S7FoNi9sFYINDPitOaBwA== + dependencies: + "@types/component-emitter" "*" + "@types/jsonwebtoken" "*" + "@types/sc-auth" "*" + "@types/sc-broker-cluster" "^6" + "@types/ws" "*" + +"@types/socketcluster@*": + version "14.0.3" + resolved "https://registry.yarnpkg.com/@types/socketcluster/-/socketcluster-14.0.3.tgz#62a89c3c08c2ee0fca5c265263e3a15fb544dd83" + integrity sha512-E+myXJK1zKtqydI+qWUxthvi4Z76+Ovzz5ijIa/yR8hfOvk1K7VhsbyNrnrk8KWLtJFpostisbPp8eLBAQrnkA== + dependencies: + "@types/sc-auth" "*" + "@types/sc-broker-cluster" "^6" + "@types/socketcluster-server" "^14" + "@types/source-list-map@*": version "0.1.2" resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" @@ -4140,7 +4252,7 @@ "@types/webpack-sources" "*" source-map "^0.6.0" -"@types/ws@^7.0.0": +"@types/ws@*", "@types/ws@^7.0.0": version "7.2.7" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.2.7.tgz#362ad1a1d62721bdb725e72c8cccf357078cf5a3" integrity sha512-UUFC/xxqFLP17hTva8/lVT0SybLUrfSD9c+iapKb0fEiC8uoDbA+xuZ3pAN603eW+bY8ebSMLm9jXdIPnD0ZgA==