mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2025-01-31 11:51:41 +03:00
feature(redux-devtools): convert to TypeScript (#605)
This commit is contained in:
parent
07a9919a68
commit
97adc01b78
|
@ -8,9 +8,6 @@
|
|||
"@babel/preset-typescript": "^7.10.4",
|
||||
"@types/jest": "^26.0.9",
|
||||
"@types/node": "^14.6.0",
|
||||
"@types/react": "^16.9.46",
|
||||
"@types/react-dom": "^16.9.8",
|
||||
"@types/react-test-renderer": "^16.9.3",
|
||||
"@types/webpack": "^4.41.21",
|
||||
"@types/webpack-dev-server": "^3.11.0",
|
||||
"@typescript-eslint/eslint-plugin": "^3.9.0",
|
||||
|
@ -25,9 +22,6 @@
|
|||
"jest": "^26.2.2",
|
||||
"lerna": "^3.22.1",
|
||||
"prettier": "^2.0.5",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-test-renderer": "^16.13.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"ts-jest": "^26.2.0",
|
||||
"ts-node": "^9.0.0",
|
||||
|
|
|
@ -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"]
|
||||
}
|
||||
|
|
3
packages/redux-devtools/.eslintignore
Normal file
3
packages/redux-devtools/.eslintignore
Normal file
|
@ -0,0 +1,3 @@
|
|||
examples
|
||||
lib
|
||||
umd
|
21
packages/redux-devtools/.eslintrc.js
Normal file
21
packages/redux-devtools/.eslintrc.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
module.exports = {
|
||||
extends: '../../.eslintrc',
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.ts', '*.tsx'],
|
||||
extends: '../../eslintrc.ts.react.base.json',
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: ['./tsconfig.json'],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['test/*.ts', 'test/*.tsx'],
|
||||
extends: '../../eslintrc.ts.react.jest.base.json',
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: ['./test/tsconfig.json'],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
18
packages/redux-devtools/index.d.ts
vendored
18
packages/redux-devtools/index.d.ts
vendored
|
@ -1,18 +0,0 @@
|
|||
// Redux version 4.0.0
|
||||
// Type definitions for redux-devtools 3.4.1
|
||||
// TypeScript Version: 2.8.1
|
||||
|
||||
import * as React from 'react';
|
||||
import { StoreEnhancer } from 'redux';
|
||||
|
||||
export interface DevTools {
|
||||
new (): JSX.ElementClass;
|
||||
instrument(opts?: any): StoreEnhancer;
|
||||
}
|
||||
|
||||
export declare function createDevTools(el: React.ReactElement<any>): DevTools;
|
||||
export declare function persistState(debugSessionKey: string): StoreEnhancer;
|
||||
|
||||
declare const factory: { instrument(opts?: any): () => StoreEnhancer };
|
||||
|
||||
export default factory;
|
3
packages/redux-devtools/jest.config.js
Normal file
3
packages/redux-devtools/jest.config.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
};
|
|
@ -2,22 +2,6 @@
|
|||
"name": "redux-devtools",
|
||||
"version": "3.6.1",
|
||||
"description": "Redux DevTools with hot reloading and time travel",
|
||||
"main": "lib/index.js",
|
||||
"scripts": {
|
||||
"clean": "rimraf lib",
|
||||
"build": "babel src --out-dir lib",
|
||||
"test": "jest",
|
||||
"prepare": "npm run build",
|
||||
"prepublishOnly": "npm run test && npm run clean && npm run build"
|
||||
},
|
||||
"files": [
|
||||
"lib",
|
||||
"src"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/reduxjs/redux-devtools.git"
|
||||
},
|
||||
"keywords": [
|
||||
"redux",
|
||||
"devtools",
|
||||
|
@ -26,34 +10,52 @@
|
|||
"time travel",
|
||||
"live edit"
|
||||
],
|
||||
"author": "Dan Abramov <dan.abramov@me.com> (http://github.com/gaearon)",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/reduxjs/redux-devtools/tree/master/packages/redux-devtools",
|
||||
"bugs": {
|
||||
"url": "https://github.com/reduxjs/redux-devtools/issues"
|
||||
},
|
||||
"homepage": "https://github.com/reduxjs/redux-devtools",
|
||||
"license": "MIT",
|
||||
"author": "Dan Abramov <dan.abramov@me.com> (http://github.com/gaearon)",
|
||||
"files": [
|
||||
"lib",
|
||||
"src"
|
||||
],
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/reduxjs/redux-devtools.git"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "npm run build:types && npm run build:js",
|
||||
"build:types": "tsc --emitDeclarationOnly",
|
||||
"build:js": "babel src --out-dir lib --extensions \".ts,.tsx\" --source-maps inline",
|
||||
"clean": "rimraf lib",
|
||||
"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": {
|
||||
"@types/prop-types": "^15.7.3",
|
||||
"lodash": "^4.17.19",
|
||||
"prop-types": "^15.7.2",
|
||||
"redux-devtools-instrument": "^1.9.7"
|
||||
},
|
||||
"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",
|
||||
"jest": "^26.2.2",
|
||||
"@types/lodash": "^4.14.159",
|
||||
"@types/react": "^16.3.18",
|
||||
"@types/react-redux": "^7.1.9",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-redux": "^7.2.1",
|
||||
"redux": "^4.0.5",
|
||||
"rimraf": "^3.0.2"
|
||||
"redux": "^4.0.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^0.14.9 || ^15.3.0 || ^16.0.0",
|
||||
"react-redux": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0",
|
||||
"redux": "^3.5.2 || ^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"lodash": "^4.17.19",
|
||||
"prop-types": "^15.7.2",
|
||||
"redux-devtools-instrument": "^1.9.7"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
import React, { Children, Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect, Provider, ReactReduxContext } from 'react-redux';
|
||||
import instrument from 'redux-devtools-instrument';
|
||||
import instrument, {
|
||||
EnhancedStore,
|
||||
LiftedState,
|
||||
LiftedStore,
|
||||
Options,
|
||||
} from 'redux-devtools-instrument';
|
||||
import { Action } from 'redux';
|
||||
|
||||
function logError(type) {
|
||||
/* eslint-disable no-console */
|
||||
function logError(type: string) {
|
||||
if (type === 'NoStore') {
|
||||
console.error(
|
||||
'Redux DevTools could not render. You must pass the Redux store ' +
|
||||
|
@ -18,16 +23,51 @@ function logError(type) {
|
|||
'using createStore()?'
|
||||
);
|
||||
}
|
||||
/* eslint-enable no-console */
|
||||
}
|
||||
|
||||
export default function createDevTools(children) {
|
||||
interface Props<
|
||||
S,
|
||||
A extends Action<unknown>,
|
||||
MonitorState,
|
||||
MonitorAction extends Action<unknown>
|
||||
> {
|
||||
store?: EnhancedStore<S, A, MonitorState>;
|
||||
}
|
||||
|
||||
type Monitor<
|
||||
S,
|
||||
A extends Action<unknown>,
|
||||
MonitorProps,
|
||||
MonitorState,
|
||||
MonitorAction extends Action<unknown>
|
||||
> = React.ReactElement<
|
||||
MonitorProps,
|
||||
React.ComponentType<MonitorProps & LiftedState<S, A, MonitorState>> & {
|
||||
update(
|
||||
monitorProps: MonitorProps,
|
||||
state: MonitorState | undefined,
|
||||
action: MonitorAction
|
||||
): MonitorState;
|
||||
}
|
||||
>;
|
||||
|
||||
export default function createDevTools<
|
||||
S,
|
||||
A extends Action<unknown>,
|
||||
MonitorProps,
|
||||
MonitorState,
|
||||
MonitorAction extends Action<unknown>
|
||||
>(children: Monitor<S, A, MonitorProps, MonitorState, MonitorAction>) {
|
||||
const monitorElement = Children.only(children);
|
||||
const monitorProps = monitorElement.props;
|
||||
const Monitor = monitorElement.type;
|
||||
const ConnectedMonitor = connect((state) => state)(Monitor);
|
||||
const ConnectedMonitor = connect(
|
||||
(state: LiftedState<S, A, MonitorState>) => state
|
||||
)(Monitor as React.ComponentType<any>);
|
||||
|
||||
return class DevTools extends Component {
|
||||
return class DevTools extends Component<
|
||||
Props<S, A, MonitorState, MonitorAction>
|
||||
> {
|
||||
static contextTypes = {
|
||||
store: PropTypes.object,
|
||||
};
|
||||
|
@ -36,13 +76,18 @@ export default function createDevTools(children) {
|
|||
store: PropTypes.object,
|
||||
};
|
||||
|
||||
static instrument = (options) =>
|
||||
liftedStore?: LiftedStore<S, A, MonitorState>;
|
||||
|
||||
static instrument = (options: Options<S, A, MonitorState, MonitorAction>) =>
|
||||
instrument(
|
||||
(state, action) => Monitor.update(monitorProps, state, action),
|
||||
options
|
||||
);
|
||||
|
||||
constructor(props, context) {
|
||||
constructor(
|
||||
props: Props<S, A, MonitorState, MonitorAction>,
|
||||
context: { store?: EnhancedStore<S, A, MonitorState> }
|
||||
) {
|
||||
super(props, context);
|
||||
|
||||
if (ReactReduxContext) {
|
||||
|
@ -60,7 +105,7 @@ export default function createDevTools(children) {
|
|||
if (context.store) {
|
||||
this.liftedStore = context.store.liftedStore;
|
||||
} else {
|
||||
this.liftedStore = props.store.liftedStore;
|
||||
this.liftedStore = props.store!.liftedStore;
|
||||
}
|
||||
|
||||
if (!this.liftedStore) {
|
||||
|
@ -88,12 +133,23 @@ export default function createDevTools(children) {
|
|||
logError('NoStore');
|
||||
return null;
|
||||
}
|
||||
if (!props.store.liftedStore) {
|
||||
if (
|
||||
!((props.store as unknown) as EnhancedStore<S, A, MonitorState>)
|
||||
.liftedStore
|
||||
) {
|
||||
logError('NoLiftedStore');
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Provider store={props.store.liftedStore}>
|
||||
<Provider
|
||||
store={
|
||||
((props.store as unknown) as EnhancedStore<
|
||||
S,
|
||||
A,
|
||||
MonitorState
|
||||
>).liftedStore
|
||||
}
|
||||
>
|
||||
<ConnectedMonitor {...monitorProps} />
|
||||
</Provider>
|
||||
);
|
|
@ -1,16 +1,24 @@
|
|||
import mapValues from 'lodash/mapValues';
|
||||
import identity from 'lodash/identity';
|
||||
import { Action, PreloadedState, Reducer, StoreEnhancer } from 'redux';
|
||||
import { LiftedState } from 'redux-devtools-instrument';
|
||||
|
||||
export default function persistState(
|
||||
sessionId,
|
||||
deserializeState = identity,
|
||||
deserializeAction = identity
|
||||
) {
|
||||
export default function persistState<
|
||||
S,
|
||||
A extends Action<unknown>,
|
||||
MonitorState
|
||||
>(
|
||||
sessionId?: string,
|
||||
deserializeState: (state: S) => S = identity,
|
||||
deserializeAction: (action: A) => A = identity
|
||||
): StoreEnhancer {
|
||||
if (!sessionId) {
|
||||
return (next) => (...args) => next(...args);
|
||||
}
|
||||
|
||||
function deserialize(state) {
|
||||
function deserialize(
|
||||
state: LiftedState<S, A, MonitorState>
|
||||
): LiftedState<S, A, MonitorState> {
|
||||
return {
|
||||
...state,
|
||||
actionsById: mapValues(state.actionsById, (liftedAction) => ({
|
||||
|
@ -25,7 +33,10 @@ export default function persistState(
|
|||
};
|
||||
}
|
||||
|
||||
return (next) => (reducer, initialState, enhancer) => {
|
||||
return (next) => <S, A extends Action<unknown>>(
|
||||
reducer: Reducer<S, A>,
|
||||
initialState?: PreloadedState<S>
|
||||
) => {
|
||||
const key = `redux-dev-session-${sessionId}`;
|
||||
|
||||
let finalInitialState;
|
||||
|
@ -44,11 +55,14 @@ export default function persistState(
|
|||
}
|
||||
}
|
||||
|
||||
const store = next(reducer, finalInitialState, enhancer);
|
||||
const store = next(
|
||||
reducer,
|
||||
finalInitialState as PreloadedState<S> | undefined
|
||||
);
|
||||
|
||||
return {
|
||||
...store,
|
||||
dispatch(action) {
|
||||
dispatch<T extends A>(action: T) {
|
||||
store.dispatch(action);
|
||||
|
||||
try {
|
5
packages/redux-devtools/test/globalLocalStorage.d.ts
vendored
Normal file
5
packages/redux-devtools/test/globalLocalStorage.d.ts
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
declare namespace NodeJS {
|
||||
interface Global {
|
||||
localStorage: Storage;
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@ import { instrument, persistState } from '../src';
|
|||
import { compose, createStore } from 'redux';
|
||||
|
||||
describe('persistState', () => {
|
||||
let savedLocalStorage = global.localStorage;
|
||||
const savedLocalStorage = global.localStorage;
|
||||
delete global.localStorage;
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -20,6 +20,12 @@ describe('persistState', () => {
|
|||
clear() {
|
||||
this.store = {};
|
||||
},
|
||||
get length() {
|
||||
return this.store.length;
|
||||
},
|
||||
key(index) {
|
||||
throw new Error('Unimplemented');
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -27,7 +33,8 @@ describe('persistState', () => {
|
|||
global.localStorage = savedLocalStorage;
|
||||
});
|
||||
|
||||
const reducer = (state = 0, action) => {
|
||||
type Action = { type: 'INCREMENT' } | { type: 'DECREMENT' };
|
||||
const reducer = (state = 0, action: Action) => {
|
||||
switch (action.type) {
|
||||
case 'INCREMENT':
|
||||
return state + 1;
|
||||
|
@ -69,7 +76,8 @@ describe('persistState', () => {
|
|||
});
|
||||
|
||||
it('should run with a custom state deserializer', () => {
|
||||
const oneLess = (state) => (state === undefined ? -1 : state - 1);
|
||||
const oneLess = (state: number | undefined) =>
|
||||
state === undefined ? -1 : state - 1;
|
||||
const store = createStore(
|
||||
reducer,
|
||||
compose(instrument(), persistState('id', oneLess))
|
||||
|
@ -88,8 +96,8 @@ describe('persistState', () => {
|
|||
});
|
||||
|
||||
it('should run with a custom action deserializer', () => {
|
||||
const incToDec = (action) =>
|
||||
action.type === 'INCREMENT' ? { type: 'DECREMENT' } : action;
|
||||
const incToDec = (action: Action) =>
|
||||
action.type === 'INCREMENT' ? ({ type: 'DECREMENT' } as const) : action;
|
||||
const store = createStore(
|
||||
reducer,
|
||||
compose(instrument(), persistState('id', undefined, incToDec))
|
||||
|
@ -108,7 +116,9 @@ describe('persistState', () => {
|
|||
});
|
||||
|
||||
it('should warn if read from localStorage fails', () => {
|
||||
const spy = jest.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
const spy = jest.spyOn(console, 'warn').mockImplementation(() => {
|
||||
// noop
|
||||
});
|
||||
delete global.localStorage.getItem;
|
||||
createStore(reducer, compose(instrument(), persistState('id')));
|
||||
|
||||
|
@ -120,7 +130,9 @@ describe('persistState', () => {
|
|||
});
|
||||
|
||||
it('should warn if write to localStorage fails', () => {
|
||||
const spy = jest.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
const spy = jest.spyOn(console, 'warn').mockImplementation(() => {
|
||||
// noop
|
||||
});
|
||||
delete global.localStorage.setItem;
|
||||
const store = createStore(
|
||||
reducer,
|
4
packages/redux-devtools/test/tsconfig.json
Normal file
4
packages/redux-devtools/test/tsconfig.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"extends": "../../../tsconfig.react.base.json",
|
||||
"include": ["../src", "."]
|
||||
}
|
7
packages/redux-devtools/tsconfig.json
Normal file
7
packages/redux-devtools/tsconfig.json
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"extends": "../../tsconfig.react.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "lib"
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
24
yarn.lock
24
yarn.lock
|
@ -3136,6 +3136,14 @@
|
|||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/hoist-non-react-statics@^3.3.0":
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
|
||||
integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
hoist-non-react-statics "^3.3.0"
|
||||
|
||||
"@types/html-minifier-terser@^5.0.0":
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.1.0.tgz#551a4589b6ee2cc9c1dff08056128aec29b94880"
|
||||
|
@ -3209,7 +3217,7 @@
|
|||
dependencies:
|
||||
"@types/lodash" "*"
|
||||
|
||||
"@types/lodash@*":
|
||||
"@types/lodash@*", "@types/lodash@^4.14.159":
|
||||
version "4.14.159"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.159.tgz#61089719dc6fdd9c5cb46efc827f2571d1517065"
|
||||
integrity sha512-gF7A72f7WQN33DpqOWw9geApQPh4M3PxluMtaHxWHXEGSN12/WbcEk/eNSqWNQcQhF66VSZ06vCF94CrHwXJDg==
|
||||
|
@ -3286,6 +3294,16 @@
|
|||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-redux@^7.1.9":
|
||||
version "7.1.9"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.9.tgz#280c13565c9f13ceb727ec21e767abe0e9b4aec3"
|
||||
integrity sha512-mpC0jqxhP4mhmOl3P4ipRsgTgbNofMRXJb08Ms6gekViLj61v1hOZEKWDCyWsdONr6EjEA6ZHXC446wdywDe0w==
|
||||
dependencies:
|
||||
"@types/hoist-non-react-statics" "^3.3.0"
|
||||
"@types/react" "*"
|
||||
hoist-non-react-statics "^3.3.0"
|
||||
redux "^4.0.0"
|
||||
|
||||
"@types/react-test-renderer@^16.9.3":
|
||||
version "16.9.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-16.9.3.tgz#96bab1860904366f4e848b739ba0e2f67bcae87e"
|
||||
|
@ -3300,7 +3318,7 @@
|
|||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react@*", "@types/react@^16.9.11", "@types/react@^16.9.35", "@types/react@^16.9.46":
|
||||
"@types/react@*", "@types/react@^16.3.18", "@types/react@^16.9.11", "@types/react@^16.9.35", "@types/react@^16.9.46":
|
||||
version "16.9.46"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.46.tgz#f0326cd7adceda74148baa9bff6e918632f5069e"
|
||||
integrity sha512-dbHzO3aAq1lB3jRQuNpuZ/mnu+CdD3H0WVaaBQA8LTT3S33xhVBUj232T8M3tAhSWJs/D/UqORYUlJNl/8VQZg==
|
||||
|
@ -13717,7 +13735,7 @@ redux-thunk@^2.3.0:
|
|||
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622"
|
||||
integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==
|
||||
|
||||
redux@^4.0.1, redux@^4.0.5:
|
||||
redux@^4.0.0, redux@^4.0.1, redux@^4.0.5:
|
||||
version "4.0.5"
|
||||
resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f"
|
||||
integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==
|
||||
|
|
Loading…
Reference in New Issue
Block a user