This commit is contained in:
Nathan Bierema 2020-08-30 00:09:39 -04:00
parent 35fc6ec837
commit b2de052522
23 changed files with 341 additions and 135 deletions

View File

@ -142,7 +142,7 @@ const mergeStylings = (
const getStylingByKeys = (
mergedStyling: StylingConfig,
keys: (string | false) | (string | false)[],
keys: (string | false | undefined) | (string | false | undefined)[],
...args: any[]
): Styling => {
if (keys === null) {

View File

@ -27,6 +27,6 @@ export type StylingConfig = {
export type Theme = string | Base16Theme | StylingConfig;
export type StylingFunction = (
keys: (string | false) | (string | false)[],
keys: (string | false | undefined) | (string | false | undefined)[],
...rest: any[]
) => Styling;

View File

@ -5,7 +5,6 @@ const isProduction = process.env.NODE_ENV === 'production';
module.exports = {
mode: isProduction ? 'production' : 'development',
devtool: 'eval',
entry: isProduction
? ['./demo/src/index']
: [
@ -18,10 +17,6 @@ module.exports = {
filename: 'bundle.js',
publicPath: isProduction ? 'static/' : '/static/',
},
plugins: isProduction ? [] : [new webpack.HotModuleReplacementPlugin()],
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
module: {
rules: [
{
@ -34,6 +29,10 @@ module.exports = {
},
],
},
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
plugins: isProduction ? [] : [new webpack.HotModuleReplacementPlugin()],
devServer: isProduction
? null
: {
@ -46,4 +45,5 @@ module.exports = {
},
historyApiFallback: true,
},
devtool: 'eval-source-map',
};

View File

@ -53,6 +53,7 @@
},
"devDependencies": {
"@types/dateformat": "^3.0.1",
"@types/hex-rgba": "^1.0.0",
"@types/lodash.debounce": "^4.0.6",
"@types/react": "^16.9.46",
"@types/react-dragula": "^1.1.0",

View File

@ -164,18 +164,20 @@ export default class ActionList<
isSelected={
(startActionId !== null &&
actionId >= startActionId &&
actionId <= selectedActionId) ||
actionId <= (selectedActionId as number)) ||
actionId === selectedActionId
}
isInFuture={
actionIds.indexOf(actionId) > actionIds.indexOf(currentActionId)
}
onSelect={(e) => onSelect(e, actionId)}
onSelect={(e: React.MouseEvent<HTMLDivElement>) =>
onSelect(e, actionId)
}
timestamps={getTimestamps(actions, actionIds, actionId)}
action={actions[actionId].action}
onToggleClick={() => onToggleAction(actionId)}
onJumpClick={() => onJumpToState(actionId)}
onCommitClick={() => onCommit(actionId)}
onCommitClick={() => onCommit()}
hideActionButtons={hideActionButtons}
isSkipped={skippedActionIds.indexOf(actionId) !== -1}
/>

View File

@ -1,10 +1,24 @@
import React from 'react';
import React, { FunctionComponent } from 'react';
import PropTypes from 'prop-types';
import { StylingFunction } from 'react-base16-styling';
import RightSlider from './RightSlider';
const getActiveButtons = (hasSkippedActions) =>
[hasSkippedActions && 'Sweep', 'Commit'].filter((a) => a);
const getActiveButtons = (hasSkippedActions: boolean): ('Sweep' | 'Commit')[] =>
[hasSkippedActions && 'Sweep', 'Commit'].filter(
(a): a is 'Sweep' | 'Commit' => !!a
);
const ActionListHeader = ({
interface Props {
styling: StylingFunction;
onSearch: (value: string) => void;
onCommit: () => void;
onSweep: () => void;
hideMainButtons: boolean | undefined;
hasSkippedActions: boolean;
hasStagedActions: boolean;
}
const ActionListHeader: FunctionComponent<Props> = ({
styling,
onSearch,
hasSkippedActions,
@ -48,4 +62,14 @@ const ActionListHeader = ({
</div>
);
ActionListHeader.propTypes = {
styling: PropTypes.func.isRequired,
onSearch: PropTypes.func.isRequired,
onCommit: PropTypes.func.isRequired,
onSweep: PropTypes.func.isRequired,
hideMainButtons: PropTypes.bool,
hasSkippedActions: PropTypes.bool.isRequired,
hasStagedActions: PropTypes.bool.isRequired,
};
export default ActionListHeader;

View File

@ -1,10 +1,43 @@
import React, { Component } from 'react';
import { DEFAULT_STATE } from './redux';
import { Base16Theme } from 'redux-devtools-themes';
import { Action } from 'redux';
import { StylingFunction } from 'react-base16-styling';
import { PerformAction } from 'redux-devtools';
import { Delta } from 'jsondiffpatch';
import { DEFAULT_STATE, DevtoolsInspectorState } from './redux';
import ActionPreviewHeader from './ActionPreviewHeader';
import DiffTab from './tabs/DiffTab';
import StateTab from './tabs/StateTab';
import ActionTab from './tabs/ActionTab';
export interface TabComponentProps<S, A extends Action<unknown>> {
labelRenderer: (
keyPath: (string | number)[],
nodeType: string,
expanded: boolean,
expandable: boolean
) => React.ReactNode;
styling: StylingFunction;
computedStates: { state: S; error?: string }[];
actions: { [actionId: number]: PerformAction<A> };
selectedActionId: number | null;
startActionId: number | null;
base16Theme: Base16Theme;
invertTheme: boolean;
isWideLayout: boolean;
dataTypeKey: string | undefined;
delta: Delta | null | undefined | false;
action: A;
nextState: S;
monitorState: DevtoolsInspectorState;
updateMonitorState: (monitorState: Partial<DevtoolsInspectorState>) => void;
}
export interface Tab<S, A extends Action<unknown>> {
name: string;
component: React.ComponentType<TabComponentProps<S, A>>;
}
const DEFAULT_TABS = [
{
name: 'Action',
@ -20,7 +53,32 @@ const DEFAULT_TABS = [
},
];
class ActionPreview extends Component {
interface Props<S, A extends Action<unknown>> {
base16Theme: Base16Theme;
invertTheme: boolean;
isWideLayout: boolean;
tabs: Tab<S, A>[] | ((tabs: Tab<S, A>[]) => Tab<S, A>[]);
tabName: string;
delta: Delta | null | undefined | false;
error: string | undefined;
nextState: S;
computedStates: { state: S; error?: string }[];
action: A;
actions: { [actionId: number]: PerformAction<A> };
selectedActionId: number | null;
startActionId: number | null;
dataTypeKey: string | undefined;
monitorState: DevtoolsInspectorState;
updateMonitorState: (monitorState: Partial<DevtoolsInspectorState>) => void;
styling: StylingFunction;
onInspectPath: (path: (string | number)[]) => void;
inspectedPath: (string | number)[];
onSelectTab: (tabName: string) => void;
}
class ActionPreview<S, A extends Action<unknown>> extends Component<
Props<S, A>
> {
static defaultProps = {
tabName: DEFAULT_STATE.tabName,
};
@ -49,21 +107,21 @@ class ActionPreview extends Component {
updateMonitorState,
} = this.props;
const renderedTabs =
const renderedTabs: Tab<S, A>[] =
typeof tabs === 'function'
? tabs(DEFAULT_TABS)
? tabs(DEFAULT_TABS as Tab<S, A>[])
: tabs
? tabs
: DEFAULT_TABS;
: (DEFAULT_TABS as Tab<S, A>[]);
const { component: TabComponent } =
renderedTabs.find((tab) => tab.name === tabName) ||
renderedTabs.find((tab) => tab.name === DEFAULT_STATE.tabName);
renderedTabs.find((tab) => tab.name === DEFAULT_STATE.tabName)!;
return (
<div key="actionPreview" {...styling('actionPreview')}>
<ActionPreviewHeader
tabs={renderedTabs}
tabs={(renderedTabs as unknown) as Tab<unknown, Action<unknown>>[]}
{...{ styling, inspectedPath, onInspectPath, tabName, onSelectTab }}
/>
{!error && (
@ -94,7 +152,11 @@ class ActionPreview extends Component {
);
}
labelRenderer = ([key, ...rest], nodeType, expanded) => {
labelRenderer = (
[key, ...rest]: (string | number)[],
nodeType: string,
expanded: boolean
) => {
const { styling, onInspectPath, inspectedPath } = this.props;
return (

View File

@ -1,6 +1,22 @@
import React from 'react';
import React, { FunctionComponent } from 'react';
import PropTypes from 'prop-types';
import { Action } from 'redux';
import { StylingFunction } from 'react-base16-styling';
import { Tab } from './ActionPreview';
const ActionPreviewHeader = ({
interface Props<S, A extends Action<unknown>> {
tabs: Tab<S, A>[];
styling: StylingFunction;
inspectedPath: (string | number)[];
onInspectPath: (path: (string | number)[]) => void;
tabName: string;
onSelectTab: (tabName: string) => void;
}
const ActionPreviewHeader: FunctionComponent<Props<
unknown,
Action<unknown>
>> = ({
styling,
inspectedPath,
onInspectPath,
@ -57,4 +73,13 @@ const ActionPreviewHeader = ({
</div>
);
ActionPreviewHeader.propTypes = {
tabs: PropTypes.array.isRequired,
styling: PropTypes.func.isRequired,
inspectedPath: PropTypes.array.isRequired,
onInspectPath: PropTypes.func.isRequired,
tabName: PropTypes.string.isRequired,
onSelectTab: PropTypes.func.isRequired,
};
export default ActionPreviewHeader;

View File

@ -14,7 +14,7 @@ import {
base16Themes,
} from './utils/createStylingFromTheme';
import ActionList from './ActionList';
import ActionPreview from './ActionPreview';
import ActionPreview, { Tab } from './ActionPreview';
import getInspectedState from './utils/getInspectedState';
import createDiffPatcher from './createDiffPatcher';
import {
@ -144,7 +144,7 @@ export interface DevtoolsInspectorProps<S, A extends Action<unknown>>
hideActionButtons?: boolean;
invertTheme: boolean;
dataTypeKey?: string;
tabs: unknown;
tabs: Tab<S, A>[] | ((tabs: Tab<S, A>[]) => Tab<S, A>[]);
}
interface State<S, A extends Action<unknown>> {
@ -342,7 +342,9 @@ export default class DevtoolsInspector<
monitorState={this.props.monitorState}
updateMonitorState={this.updateMonitorState}
styling={styling}
onInspectPath={this.handleInspectPath.bind(this, inspectedPathType)}
onInspectPath={(path: (string | number)[]) =>
this.handleInspectPath(inspectedPathType, path)
}
inspectedPath={monitorState[inspectedPathType]}
onSelectTab={this.handleSelectTab}
/>

View File

@ -1,7 +1,20 @@
import React from 'react';
import React, { FunctionComponent } from 'react';
import PropTypes from 'prop-types';
import { StylingFunction } from 'react-base16-styling';
const RightSlider = ({ styling, shown, children, rotate }) => (
interface Props {
styling: StylingFunction;
shown?: boolean;
children: React.ReactNode;
rotate?: boolean;
}
const RightSlider: FunctionComponent<Props> = ({
styling,
shown,
children,
rotate,
}) => (
<div
{...styling([
'rightSlider',
@ -15,7 +28,10 @@ const RightSlider = ({ styling, shown, children, rotate }) => (
);
RightSlider.propTypes = {
styling: PropTypes.func.isRequired,
shown: PropTypes.bool,
children: PropTypes.any.isRequired,
rotate: PropTypes.bool,
};
export default RightSlider;

View File

@ -1,28 +1,37 @@
import { DiffPatcher } from 'jsondiffpatch';
import { DiffContext, DiffPatcher } from 'jsondiffpatch';
const defaultObjectHash = (o, idx) =>
const defaultObjectHash = (o: any, idx: number) =>
(o === null && '$$null') ||
(o && (o.id || o.id === 0) && `$$id:${JSON.stringify(o.id)}`) ||
(o && (o._id || o._id === 0) && `$$_id:${JSON.stringify(o._id)}`) ||
'$$index:' + idx;
`$$index:${idx}`;
const defaultPropertyFilter = (name, context) =>
const defaultPropertyFilter = (name: string, context: DiffContext) =>
typeof context.left[name] !== 'function' &&
typeof context.right[name] !== 'function';
const defaultDiffPatcher = new DiffPatcher({
arrays: { detectMove: false },
arrays: { detectMove: false } as {
detectMove: boolean;
includeValueOnMove: boolean;
},
objectHash: defaultObjectHash,
propertyFilter: defaultPropertyFilter,
});
export default function createDiffPatcher(objectHash, propertyFilter) {
export default function createDiffPatcher(
objectHash: ((item: unknown, index: number) => string) | undefined,
propertyFilter: ((name: string, context: DiffContext) => boolean) | undefined
) {
if (!objectHash && !propertyFilter) {
return defaultDiffPatcher;
}
return new DiffPatcher({
arrays: { detectMove: false },
arrays: { detectMove: false } as {
detectMove: boolean;
includeValueOnMove: boolean;
},
objectHash: objectHash || defaultObjectHash,
propertyFilter: propertyFilter || defaultPropertyFilter,
});

View File

@ -1,9 +1,15 @@
import React from 'react';
import React, { FunctionComponent } from 'react';
import PropTypes from 'prop-types';
import JSONTree from 'react-json-tree';
import { Action } from 'redux';
import getItemString from './getItemString';
import getJsonTreeTheme from './getJsonTreeTheme';
import { TabComponentProps } from '../ActionPreview';
const ActionTab = ({
const ActionTab: FunctionComponent<TabComponentProps<
unknown,
Action<unknown>
>> = ({
action,
styling,
base16Theme,
@ -24,4 +30,14 @@ const ActionTab = ({
/>
);
ActionTab.propTypes = {
action: PropTypes.any.isRequired,
styling: PropTypes.func.isRequired,
base16Theme: PropTypes.any.isRequired,
invertTheme: PropTypes.bool.isRequired,
labelRenderer: PropTypes.func.isRequired,
dataTypeKey: PropTypes.string,
isWideLayout: PropTypes.bool.isRequired,
};
export default ActionTab;

View File

@ -1,13 +1,20 @@
import React from 'react';
import React, { FunctionComponent } from 'react';
import PropTypes from 'prop-types';
import JSONDiff from './JSONDiff';
import { TabComponentProps } from '../ActionPreview';
import { Action } from 'redux';
const DiffTab = ({
const DiffTab: FunctionComponent<TabComponentProps<
unknown,
Action<unknown>
>> = ({
delta,
styling,
base16Theme,
invertTheme,
labelRenderer,
isWideLayout,
dataTypeKey,
}) => (
<JSONDiff
{...{
@ -17,8 +24,19 @@ const DiffTab = ({
invertTheme,
labelRenderer,
isWideLayout,
dataTypeKey,
}}
/>
);
DiffTab.propTypes = {
delta: PropTypes.any,
styling: PropTypes.func.isRequired,
base16Theme: PropTypes.any.isRequired,
invertTheme: PropTypes.bool.isRequired,
labelRenderer: PropTypes.func.isRequired,
isWideLayout: PropTypes.bool.isRequired,
dataTypeKey: PropTypes.string,
};
export default DiffTab;

View File

@ -1,10 +1,13 @@
import React, { Component } from 'react';
import JSONTree from 'react-json-tree';
import { stringify } from 'javascript-stringify';
import { Delta } from 'jsondiffpatch';
import { StylingFunction } from 'react-base16-styling';
import { Base16Theme } from 'redux-devtools-themes';
import getItemString from './getItemString';
import getJsonTreeTheme from './getJsonTreeTheme';
function stringifyAndShrink(val, isWideLayout) {
function stringifyAndShrink(val: any, isWideLayout?: boolean) {
if (val === null) {
return 'null';
}
@ -19,12 +22,16 @@ function stringifyAndShrink(val, isWideLayout) {
return str.length > 22 ? `${str.substr(0, 15)}${str.substr(-5)}` : str;
}
const expandFirstLevel = (keyName, data, level) => level <= 1;
const expandFirstLevel = (
keyName: (string | number)[],
data: any,
level: number
) => level <= 1;
function prepareDelta(value) {
function prepareDelta(value: any) {
if (value && value._t === 'a') {
const res = {};
for (let key in value) {
const res: { [key: string]: any } = {};
for (const key in value) {
if (key !== '_t') {
if (key[0] === '_' && !value[key.substr(1)]) {
res[key.substr(1)] = value[key];
@ -41,14 +48,33 @@ function prepareDelta(value) {
return value;
}
export default class JSONDiff extends Component {
state = { data: {} };
interface Props {
delta: Delta | null | undefined | false;
styling: StylingFunction;
base16Theme: Base16Theme;
invertTheme: boolean;
labelRenderer: (
keyPath: (string | number)[],
nodeType: string,
expanded: boolean,
expandable: boolean
) => React.ReactNode;
isWideLayout: boolean;
dataTypeKey: string | undefined;
}
interface State {
data: any;
}
export default class JSONDiff extends Component<Props, State> {
state: State = { data: {} };
componentDidMount() {
this.updateData();
}
componentDidUpdate(prevProps) {
componentDidUpdate(prevProps: Props) {
if (prevProps.delta !== this.props.delta) {
this.updateData();
}
@ -84,7 +110,7 @@ export default class JSONDiff extends Component {
);
}
getItemString = (type, data) =>
getItemString = (type: string, data: any) =>
getItemString(
this.props.styling,
type,
@ -94,10 +120,10 @@ export default class JSONDiff extends Component {
true
);
valueRenderer = (raw, value) => {
valueRenderer = (raw: any, value: any) => {
const { styling, isWideLayout } = this.props;
function renderSpan(name, body) {
function renderSpan(name: string, body: string) {
return (
<span key={name} {...styling(['diff', name])}>
{body}

View File

@ -1,9 +1,15 @@
import React from 'react';
import PropTypes from 'prop-types';
import JSONTree from 'react-json-tree';
import { Action } from 'redux';
import getItemString from './getItemString';
import getJsonTreeTheme from './getJsonTreeTheme';
import { TabComponentProps } from '../ActionPreview';
const StateTab = ({
const StateTab: React.FunctionComponent<TabComponentProps<
any,
Action<unknown>
>> = ({
nextState,
styling,
base16Theme,
@ -24,4 +30,14 @@ const StateTab = ({
/>
);
StateTab.propTypes = {
nextState: PropTypes.any.isRequired,
styling: PropTypes.func.isRequired,
base16Theme: PropTypes.any.isRequired,
invertTheme: PropTypes.bool.isRequired,
labelRenderer: PropTypes.func.isRequired,
dataTypeKey: PropTypes.string,
isWideLayout: PropTypes.bool.isRequired,
};
export default StateTab;

View File

@ -1,18 +1,15 @@
import React from 'react';
import { Iterable } from 'immutable';
import { isCollection, isIndexed, isKeyed } from 'immutable';
import { StylingFunction } from 'react-base16-styling';
import isIterable from '../utils/isIterable';
const IS_IMMUTABLE_KEY = '@@__IS_IMMUTABLE__@@';
function isImmutable(value) {
return (
Iterable.isKeyed(value) ||
Iterable.isIndexed(value) ||
Iterable.isIterable(value)
);
function isImmutable(value: any) {
return isKeyed(value) || isIndexed(value) || isCollection(value);
}
function getShortTypeString(val, diff) {
function getShortTypeString(val: any, diff: boolean | undefined) {
if (diff && Array.isArray(val)) {
val = val[val.length === 2 ? 1 : 0];
}
@ -38,14 +35,21 @@ function getShortTypeString(val, diff) {
}
}
function getText(type, data, isWideLayout, isDiff) {
function getText(
type: string,
data: any,
isWideLayout: boolean,
isDiff: boolean | undefined
) {
if (type === 'Object') {
const keys = Object.keys(data);
if (!isWideLayout) return keys.length ? '{…}' : '{}';
const str = keys
.slice(0, 3)
.map((key) => `${key}: ${getShortTypeString(data[key], isDiff)}`)
.map(
(key) => `${key}: ${getShortTypeString(data[key], isDiff) as string}`
)
.concat(keys.length > 3 ? ['…'] : [])
.join(', ');
@ -55,27 +59,27 @@ function getText(type, data, isWideLayout, isDiff) {
const str = data
.slice(0, 4)
.map((val) => getShortTypeString(val, isDiff))
.map((val: any) => getShortTypeString(val, isDiff))
.concat(data.length > 4 ? ['…'] : [])
.join(', ');
return `[${str}]`;
return `[${str as string}]`;
} else {
return type;
}
}
const getItemString = (
styling,
type,
data,
dataTypeKey,
isWideLayout,
isDiff
styling: StylingFunction,
type: string,
data: any,
dataTypeKey: string | undefined,
isWideLayout: boolean,
isDiff?: boolean
) => (
<span {...styling('treeItemHint')}>
{data[IS_IMMUTABLE_KEY] ? 'Immutable' : ''}
{dataTypeKey && data[dataTypeKey] ? data[dataTypeKey] + ' ' : ''}
{dataTypeKey && data[dataTypeKey] ? `${data[dataTypeKey] as string} ` : ''}
{getText(type, data, isWideLayout, isDiff)}
</span>
);

View File

@ -1,4 +1,9 @@
export default function getJsonTreeTheme(base16Theme) {
import { Base16Theme } from 'base16';
import { StylingConfig } from 'react-base16-styling';
export default function getJsonTreeTheme(
base16Theme: Base16Theme
): StylingConfig {
return {
extend: base16Theme,
nestedNode: ({ style }, keyPath, nodeType, expanded) => ({

View File

@ -1,14 +1,15 @@
import jss from 'jss';
import jss, { Styles, StyleSheet } from 'jss';
import preset from 'jss-preset-default';
import { createStyling } from 'react-base16-styling';
import rgba from 'hex-rgba';
import { Base16Theme } from 'redux-devtools-themes';
import inspector from '../themes/inspector';
import * as reduxThemes from 'redux-devtools-themes';
import * as inspectorThemes from '../themes';
jss.setup(preset());
const colorMap = (theme) => ({
const colorMap = (theme: Base16Theme) => ({
TEXT_COLOR: theme.base06,
TEXT_PLACEHOLDER_COLOR: rgba(theme.base06, 60),
BACKGROUND_COLOR: theme.base00,
@ -34,7 +35,12 @@ const colorMap = (theme) => ({
ERROR_COLOR: theme.base08,
});
const getSheetFromColorMap = (map) => ({
type Color = keyof ReturnType<typeof colorMap>;
type ColorMap = {
[color in Color]: string;
};
const getSheetFromColorMap = (map: ColorMap) => ({
inspector: {
display: 'flex',
'flex-direction': 'column',
@ -384,9 +390,9 @@ const getSheetFromColorMap = (map) => ({
},
});
let themeSheet;
let themeSheet: StyleSheet;
const getDefaultThemeStyling = (theme) => {
const getDefaultThemeStyling = (theme: Base16Theme) => {
if (themeSheet) {
themeSheet.detach();
}

View File

@ -1,30 +0,0 @@
function deepMapCached(obj, f, ctx, cache) {
cache.push(obj);
if (Array.isArray(obj)) {
return obj.map(function (val, key) {
val = f.call(ctx, val, key);
return typeof val === 'object' && cache.indexOf(val) === -1
? deepMapCached(val, f, ctx, cache)
: val;
});
} else if (typeof obj === 'object') {
const res = {};
for (const key in obj) {
let val = obj[key];
if (val && typeof val === 'object') {
val = f.call(ctx, val, key);
res[key] =
cache.indexOf(val) === -1 ? deepMapCached(val, f, ctx, cache) : val;
} else {
res[key] = f.call(ctx, val, key);
}
}
return res;
} else {
return obj;
}
}
export default function deepMap(obj, f, ctx) {
return deepMapCached(obj, f, ctx, []);
}

View File

@ -1,10 +1,10 @@
import { Iterable, fromJS } from 'immutable';
import { fromJS, isAssociative } from 'immutable';
import isIterable from './isIterable';
function iterateToKey(obj, key) {
function iterateToKey(obj: any, key: string | number) {
// maybe there's a better way, dunno
let idx = 0;
for (let entry of obj) {
for (const entry of obj) {
if (Array.isArray(entry)) {
if (entry[0] === key) return entry[1];
} else {
@ -15,24 +15,28 @@ function iterateToKey(obj, key) {
}
}
export default function getInspectedState(state, path, convertImmutable) {
export default function getInspectedState<S>(
state: S,
path: (string | number)[],
convertImmutable: boolean
): S {
state =
path && path.length
? {
[path[path.length - 1]]: path.reduce((s, key) => {
? ({
[path[path.length - 1]]: path.reduce((s: any, key) => {
if (!s) {
return s;
}
if (Iterable.isAssociative(s)) {
return s.get(key);
if (isAssociative(s)) {
return s.get(key as number);
} else if (isIterable(s)) {
return iterateToKey(s, key);
}
return s[key];
}, state),
}
} as S)
: state;
if (convertImmutable) {

View File

@ -1,4 +1,4 @@
export default function isIterable(obj) {
export default function isIterable(obj: any) {
return (
obj !== null &&
typeof obj === 'object' &&

View File

@ -9,7 +9,6 @@ const isProduction = process.env.NODE_ENV === 'production';
module.exports = {
mode: process.env.NODE_ENV || 'development',
devtool: 'eval-source-map',
entry: isProduction
? ['./demo/src/js/index']
: [
@ -21,26 +20,10 @@ module.exports = {
path: path.join(__dirname, 'demo/dist'),
filename: 'js/bundle.js',
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
inject: true,
template: 'demo/src/index.html',
package: pkg,
}),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV),
},
}),
].concat(isProduction ? [] : [new webpack.HotModuleReplacementPlugin()]),
resolve: {
extensions: ['*', '.js', '.jsx'],
},
module: {
rules: [
{
test: /\.jsx?$/,
test: /\.(js|ts)x?$/,
loader: 'babel-loader',
include: [
path.join(__dirname, 'src'),
@ -49,6 +32,17 @@ module.exports = {
},
],
},
resolve: {
extensions: ['*', '.js', '.jsx'],
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
inject: true,
template: 'demo/src/index.html',
package: pkg,
}),
].concat(isProduction ? [] : [new webpack.HotModuleReplacementPlugin()]),
devServer: isProduction
? {}
: {
@ -61,4 +55,5 @@ module.exports = {
},
historyApiFallback: true,
},
devtool: 'eval-source-map',
};

View File

@ -3131,6 +3131,11 @@
dependencies:
"@types/node" "*"
"@types/hex-rgba@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@types/hex-rgba/-/hex-rgba-1.0.0.tgz#b2aed2aa9fdd6152b7f0ac5e3733b974d4eba35a"
integrity sha512-u3AGV8fjRsDBqY4wOvVWhVCgKDfh2b0h3mux7KPKU1cm/6mJp14OWBINLgBypeBTM89Nm2j+eKQqIoIe7150DA==
"@types/hoist-non-react-statics@*", "@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"