mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2025-07-27 00:19:55 +03:00
Convert redux-devtools package to TypeScript
This commit is contained in:
parent
93d6bc4350
commit
8eb67453aa
|
@ -1,4 +1,10 @@
|
|||
{
|
||||
"presets": ["@babel/preset-env", "@babel/preset-react"],
|
||||
"plugins": ["@babel/plugin-proposal-class-properties"]
|
||||
"presets": [
|
||||
"@babel/env",
|
||||
"@babel/react",
|
||||
"@babel/typescript"
|
||||
],
|
||||
"plugins": [
|
||||
"@babel/proposal-class-properties"
|
||||
]
|
||||
}
|
||||
|
|
2
packages/redux-devtools/.eslintignore
Normal file
2
packages/redux-devtools/.eslintignore
Normal file
|
@ -0,0 +1,2 @@
|
|||
lib
|
||||
examples
|
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']
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
2
packages/redux-devtools/.prettierignore
Normal file
2
packages/redux-devtools/.prettierignore
Normal file
|
@ -0,0 +1,2 @@
|
|||
lib
|
||||
examples
|
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,10 +3,17 @@
|
|||
"version": "3.5.0",
|
||||
"description": "Redux DevTools with hot reloading and time travel",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
"scripts": {
|
||||
"type-check": "tsc --noEmit",
|
||||
"type-check:watch": "npm run type-check -- --watch",
|
||||
"clean": "rimraf lib",
|
||||
"build": "babel src --out-dir lib",
|
||||
"test": "jest",
|
||||
"build": "npm run build:types && npm run build:js",
|
||||
"build:types": "tsc --emitDeclarationOnly",
|
||||
"build:js": "babel src --out-dir lib --extendsions \".ts,.tsx\" --source-maps inline",
|
||||
"lint": "eslint . --ext .js,.jsx,.ts,.tsx",
|
||||
"lint:fix": "eslint . --ext .js,.jsx,.ts,.tx --fix",
|
||||
"test": "tsc --project test/tsconfig.json --noEmit && jest",
|
||||
"prepare": "npm run build",
|
||||
"prepublishOnly": "npm run test && npm run clean && npm run build"
|
||||
},
|
||||
|
@ -38,22 +45,32 @@
|
|||
"@babel/plugin-proposal-class-properties": "^7.3.0",
|
||||
"@babel/preset-env": "^7.3.1",
|
||||
"@babel/preset-react": "^7.0.0",
|
||||
"@babel/preset-typescript": "^7.9.0",
|
||||
"@types/lodash": "^4.2.0",
|
||||
"@types/react": "^0.14.9 || ^15.3.0 || ^16.0.0",
|
||||
"@types/react-redux": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^2.31.0",
|
||||
"@typescript-eslint/parser": "^2.31.0",
|
||||
"babel-loader": "^8.0.5",
|
||||
"jest": "^24.1.0",
|
||||
"react": "^16.0.0",
|
||||
"react": "^0.14.9 || ^15.3.0 || ^16.0.0",
|
||||
"react-dom": "^16.0.0",
|
||||
"react-redux": "^6.0.0",
|
||||
"redux": "^4.0.0",
|
||||
"rimraf": "^2.3.4"
|
||||
"react-redux": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0",
|
||||
"redux": "^3.5.2 || ^4.0.0",
|
||||
"rimraf": "^2.3.4",
|
||||
"typescript": "^3.8.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^0.14.9 || ^15.3.0 || ^16.0.0",
|
||||
"@types/react-redux": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0",
|
||||
"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": {
|
||||
"prop-types": "^15.5.7",
|
||||
"@types/prop-types": "^15.5.7",
|
||||
"lodash": "^4.2.0",
|
||||
"prop-types": "^15.5.7",
|
||||
"redux-devtools-instrument": "^1.9.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
const monitorElement = Children.only(children);
|
||||
interface Props<
|
||||
S,
|
||||
A extends Action,
|
||||
MonitorState,
|
||||
MonitorAction extends Action<unknown>
|
||||
> {
|
||||
store?: EnhancedStore<S, A, MonitorState, MonitorAction>;
|
||||
}
|
||||
|
||||
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, MonitorAction>;
|
||||
|
||||
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, MonitorAction> }
|
||||
) {
|
||||
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,28 @@ export default function createDevTools(children) {
|
|||
logError('NoStore');
|
||||
return null;
|
||||
}
|
||||
if (!props.store.liftedStore) {
|
||||
if (
|
||||
!((props.store as unknown) as EnhancedStore<
|
||||
S,
|
||||
A,
|
||||
MonitorState,
|
||||
MonitorAction
|
||||
>).liftedStore
|
||||
) {
|
||||
logError('NoLiftedStore');
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Provider store={props.store.liftedStore}>
|
||||
<Provider
|
||||
store={
|
||||
((props.store as unknown) as EnhancedStore<
|
||||
S,
|
||||
A,
|
||||
MonitorState,
|
||||
MonitorAction
|
||||
>).liftedStore
|
||||
}
|
||||
>
|
||||
<ConnectedMonitor {...monitorProps} />
|
||||
</Provider>
|
||||
);
|
|
@ -1,7 +1,8 @@
|
|||
export {
|
||||
default as instrument,
|
||||
ActionCreators,
|
||||
ActionTypes
|
||||
ActionTypes,
|
||||
LiftedAction
|
||||
} from 'redux-devtools-instrument';
|
||||
export { default as persistState } from './persistState';
|
||||
export { default as createDevTools } from './createDevTools';
|
|
@ -1,16 +1,18 @@
|
|||
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>>(
|
||||
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>): LiftedState<S, A> {
|
||||
return {
|
||||
...state,
|
||||
actionsById: mapValues(state.actionsById, liftedAction => ({
|
||||
|
@ -25,7 +27,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 +49,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;
|
||||
}
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
import { instrument, persistState } from '../src';
|
||||
import { compose, createStore } from 'redux';
|
||||
import './globalLocalStorage.d.ts';
|
||||
|
||||
describe('persistState', () => {
|
||||
let savedLocalStorage = global.localStorage;
|
||||
const savedLocalStorage = global.localStorage;
|
||||
delete global.localStorage;
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -19,6 +20,12 @@ describe('persistState', () => {
|
|||
},
|
||||
clear() {
|
||||
this.store = {};
|
||||
},
|
||||
get length() {
|
||||
return this.store.length;
|
||||
},
|
||||
key(index) {
|
||||
throw new Error('Unimplemented');
|
||||
}
|
||||
};
|
||||
});
|
||||
|
@ -27,7 +34,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;
|
||||
|
@ -41,10 +49,7 @@ describe('persistState', () => {
|
|||
it('should persist state', () => {
|
||||
const store = createStore(
|
||||
reducer,
|
||||
compose(
|
||||
instrument(),
|
||||
persistState('id')
|
||||
)
|
||||
compose(instrument(), persistState('id'))
|
||||
);
|
||||
expect(store.getState()).toBe(0);
|
||||
|
||||
|
@ -54,46 +59,29 @@ describe('persistState', () => {
|
|||
|
||||
const store2 = createStore(
|
||||
reducer,
|
||||
compose(
|
||||
instrument(),
|
||||
persistState('id')
|
||||
)
|
||||
compose(instrument(), persistState('id'))
|
||||
);
|
||||
expect(store2.getState()).toBe(2);
|
||||
});
|
||||
|
||||
it('should not persist state if no session id', () => {
|
||||
const store = createStore(
|
||||
reducer,
|
||||
compose(
|
||||
instrument(),
|
||||
persistState()
|
||||
)
|
||||
);
|
||||
const store = createStore(reducer, compose(instrument(), persistState()));
|
||||
expect(store.getState()).toBe(0);
|
||||
|
||||
store.dispatch({ type: 'INCREMENT' });
|
||||
store.dispatch({ type: 'INCREMENT' });
|
||||
expect(store.getState()).toBe(2);
|
||||
|
||||
const store2 = createStore(
|
||||
reducer,
|
||||
compose(
|
||||
instrument(),
|
||||
persistState()
|
||||
)
|
||||
);
|
||||
const store2 = createStore(reducer, compose(instrument(), persistState()));
|
||||
expect(store2.getState()).toBe(0);
|
||||
});
|
||||
|
||||
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)
|
||||
)
|
||||
compose(instrument(), persistState('id', oneLess))
|
||||
);
|
||||
expect(store.getState()).toBe(0);
|
||||
|
||||
|
@ -103,23 +91,17 @@ describe('persistState', () => {
|
|||
|
||||
const store2 = createStore(
|
||||
reducer,
|
||||
compose(
|
||||
instrument(),
|
||||
persistState('id', oneLess)
|
||||
)
|
||||
compose(instrument(), persistState('id', oneLess))
|
||||
);
|
||||
expect(store2.getState()).toBe(1);
|
||||
});
|
||||
|
||||
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)
|
||||
)
|
||||
compose(instrument(), persistState('id', undefined, incToDec))
|
||||
);
|
||||
expect(store.getState()).toBe(0);
|
||||
|
||||
|
@ -129,24 +111,17 @@ describe('persistState', () => {
|
|||
|
||||
const store2 = createStore(
|
||||
reducer,
|
||||
compose(
|
||||
instrument(),
|
||||
persistState('id', undefined, incToDec)
|
||||
)
|
||||
compose(instrument(), persistState('id', undefined, incToDec))
|
||||
);
|
||||
expect(store2.getState()).toBe(-2);
|
||||
});
|
||||
|
||||
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')
|
||||
)
|
||||
);
|
||||
createStore(reducer, compose(instrument(), persistState('id')));
|
||||
|
||||
expect(spy.mock.calls[0]).toContain(
|
||||
'Could not read debug session from localStorage:'
|
||||
|
@ -156,14 +131,13 @@ 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,
|
||||
compose(
|
||||
instrument(),
|
||||
persistState('id')
|
||||
)
|
||||
compose(instrument(), persistState('id'))
|
||||
);
|
||||
|
||||
store.dispatch({ type: 'INCREMENT' });
|
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"]
|
||||
}
|
Loading…
Reference in New Issue
Block a user