From 75aa663d64f9b78695538fad3d44fbff4ad1b2e7 Mon Sep 17 00:00:00 2001 From: FaberVitale Date: Sat, 12 Jun 2021 17:16:08 +0200 Subject: [PATCH] feat: complete inspector layout and add initial JSONTree setup --- .../demo/package.json | 3 +- .../demo/public/index.html | 5 +- .../demo/src/DevTools.tsx | 2 +- .../demo/src/index.tsx | 2 +- .../demo/src/store.ts | 2 +- .../demo/tsconfig.json | 9 +- .../demo/yarn.lock | 9 ++ .../package.json | 3 +- .../src/RtkQueryInspector.tsx | 76 ++++++++++--- .../src/RtkQueryInspectorMonitor.tsx | 28 ++--- .../src/components/QueryPreview.tsx | 96 ++++++++++++++++ .../src/monitor-config.ts | 1 + .../src/selectors.ts | 21 ++++ .../src/styles/createStylingFromTheme.ts | 91 ++++++++++++++- .../src/styles/tree.tsx | 104 ++++++++++++++++++ .../src/utils/isIterable.ts | 8 ++ .../tsconfig.dev.json | 17 ++- 17 files changed, 416 insertions(+), 61 deletions(-) create mode 100644 packages/redux-devtools-rtk-query-inspector-monitor/src/components/QueryPreview.tsx create mode 100644 packages/redux-devtools-rtk-query-inspector-monitor/src/monitor-config.ts create mode 100644 packages/redux-devtools-rtk-query-inspector-monitor/src/styles/tree.tsx create mode 100644 packages/redux-devtools-rtk-query-inspector-monitor/src/utils/isIterable.ts diff --git a/packages/redux-devtools-rtk-query-inspector-monitor/demo/package.json b/packages/redux-devtools-rtk-query-inspector-monitor/demo/package.json index 46619ed7..8cb0070d 100644 --- a/packages/redux-devtools-rtk-query-inspector-monitor/demo/package.json +++ b/packages/redux-devtools-rtk-query-inspector-monitor/demo/package.json @@ -17,7 +17,8 @@ "react-redux": "^7.2.1", "react-scripts": "4.0.2", "redux": "^4.0.5", - "redux-devtools-themes": "^1.0.0" + "redux-devtools-themes": "^1.0.0", + "react-json-tree": "^0.15.0" }, "devDependencies": { "@types/react": "17.0.0", diff --git a/packages/redux-devtools-rtk-query-inspector-monitor/demo/public/index.html b/packages/redux-devtools-rtk-query-inspector-monitor/demo/public/index.html index 4d6c2549..89cf6a1d 100644 --- a/packages/redux-devtools-rtk-query-inspector-monitor/demo/public/index.html +++ b/packages/redux-devtools-rtk-query-inspector-monitor/demo/public/index.html @@ -4,7 +4,10 @@ - + Snowpack App diff --git a/packages/redux-devtools-rtk-query-inspector-monitor/demo/src/DevTools.tsx b/packages/redux-devtools-rtk-query-inspector-monitor/demo/src/DevTools.tsx index d137fd11..cb48713b 100644 --- a/packages/redux-devtools-rtk-query-inspector-monitor/demo/src/DevTools.tsx +++ b/packages/redux-devtools-rtk-query-inspector-monitor/demo/src/DevTools.tsx @@ -10,5 +10,5 @@ export default createDevTools( changeMonitorKey="ctrl-m" > - , + ); diff --git a/packages/redux-devtools-rtk-query-inspector-monitor/demo/src/index.tsx b/packages/redux-devtools-rtk-query-inspector-monitor/demo/src/index.tsx index 39ef9e78..ee93252d 100644 --- a/packages/redux-devtools-rtk-query-inspector-monitor/demo/src/index.tsx +++ b/packages/redux-devtools-rtk-query-inspector-monitor/demo/src/index.tsx @@ -12,5 +12,5 @@ ReactDOM.render( , - rootElement, + rootElement ); diff --git a/packages/redux-devtools-rtk-query-inspector-monitor/demo/src/store.ts b/packages/redux-devtools-rtk-query-inspector-monitor/demo/src/store.ts index e434f6d8..409e4ef8 100644 --- a/packages/redux-devtools-rtk-query-inspector-monitor/demo/src/store.ts +++ b/packages/redux-devtools-rtk-query-inspector-monitor/demo/src/store.ts @@ -9,5 +9,5 @@ export const store = configureStore({ // adding the api middleware enables caching, invalidation, polling and other features of `rtk-query` middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(pokemonApi.middleware), - enhancers: [DevTools.instrument()] + enhancers: [DevTools.instrument()], }); diff --git a/packages/redux-devtools-rtk-query-inspector-monitor/demo/tsconfig.json b/packages/redux-devtools-rtk-query-inspector-monitor/demo/tsconfig.json index 5f488e8e..2e7afa38 100644 --- a/packages/redux-devtools-rtk-query-inspector-monitor/demo/tsconfig.json +++ b/packages/redux-devtools-rtk-query-inspector-monitor/demo/tsconfig.json @@ -1,14 +1,9 @@ { - "include": [ - "./src/**/*" - ], + "include": ["./src/**/*"], "compilerOptions": { "strict": true, "esModuleInterop": true, - "lib": [ - "dom", - "es2015" - ], + "lib": ["dom", "es2015"], "jsx": "react-jsx", "target": "es5", "allowJs": true, diff --git a/packages/redux-devtools-rtk-query-inspector-monitor/demo/yarn.lock b/packages/redux-devtools-rtk-query-inspector-monitor/demo/yarn.lock index a8a91204..6fbf3b2a 100644 --- a/packages/redux-devtools-rtk-query-inspector-monitor/demo/yarn.lock +++ b/packages/redux-devtools-rtk-query-inspector-monitor/demo/yarn.lock @@ -9411,6 +9411,15 @@ react-is@^17.0.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== +react-json-tree@^0.15.0: + version "0.15.0" + resolved "https://registry.yarnpkg.com/react-json-tree/-/react-json-tree-0.15.0.tgz#16a5bbed761f711d1656de6c62818d40ddb09442" + integrity sha512-/bEFXZBfLFiep6ReuzatR8mz9G7sRmejElRDgcAuqY0Jsx7llouax2DM03rlQifrUJgmvTGmPA+olyWYyGagqA== + dependencies: + "@types/prop-types" "^15.7.3" + prop-types "^15.7.2" + react-base16-styling "^0.8.0" + react-redux@^7.2.1: version "7.2.4" resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.4.tgz#1ebb474032b72d806de2e0519cd07761e222e225" diff --git a/packages/redux-devtools-rtk-query-inspector-monitor/package.json b/packages/redux-devtools-rtk-query-inspector-monitor/package.json index 836f444b..c2a55620 100644 --- a/packages/redux-devtools-rtk-query-inspector-monitor/package.json +++ b/packages/redux-devtools-rtk-query-inspector-monitor/package.json @@ -45,7 +45,8 @@ "@redux-devtools/dock-monitor": "^1.4.0", "prop-types": "^15.7.2", "redux-devtools-themes": "^1.0.0", - "devui": "^1.0.0-8" + "devui": "^1.0.0-8", + "react-json-tree": "^0.15.0" }, "devDependencies": { "@redux-devtools/core": "^3.9.0", diff --git a/packages/redux-devtools-rtk-query-inspector-monitor/src/RtkQueryInspector.tsx b/packages/redux-devtools-rtk-query-inspector-monitor/src/RtkQueryInspector.tsx index 2b0d06fd..68db8990 100644 --- a/packages/redux-devtools-rtk-query-inspector-monitor/src/RtkQueryInspector.tsx +++ b/packages/redux-devtools-rtk-query-inspector-monitor/src/RtkQueryInspector.tsx @@ -1,4 +1,4 @@ -import React, { Component, createRef, CSSProperties, ReactNode } from 'react'; +import React, { Component, createRef, ReactNode } from 'react'; import { AnyAction, Dispatch, Action } from 'redux'; import { LiftedAction, LiftedState } from '@redux-devtools/core'; import * as themes from 'redux-devtools-themes'; @@ -9,11 +9,7 @@ import { selectQueryKey } from './reducers'; import { QueryList } from './components/QueryList'; import { StyleUtils } from './styles/createStylingFromTheme'; import { QueryForm } from './components/QueryForm'; - -const wrapperStyle: CSSProperties = { - width: '100%', - height: '100%', -}; +import { QueryPreview } from './components/QueryPreview'; type SelectorsSource = { currentState: S | null; @@ -29,26 +25,34 @@ export interface RtkQueryInspectorProps> styleUtils: StyleUtils; } -type RtkQueryInspectorState = { selectorsSource: SelectorsSource }; +type RtkQueryInspectorState = { + selectorsSource: SelectorsSource; + isWideLayout: boolean; +}; class RtkQueryInspector> extends Component< RtkQueryInspectorProps, RtkQueryInspectorState > { - divRef = createRef(); + inspectorRef = createRef(); + + isWideIntervalRef: number | NodeJS.Timeout | null = null; constructor(props: RtkQueryInspectorProps) { super(props); this.state = { + isWideLayout: true, selectorsSource: computeSelectorSource(props, null), }; } + static wideLayout = 500; + static getDerivedStateFromProps( props: RtkQueryInspectorProps>, state: RtkQueryInspectorState - ): null | RtkQueryInspectorState { + ): null | Partial> { const selectorsSource = computeSelectorSource>( props, state.selectorsSource @@ -65,12 +69,35 @@ class RtkQueryInspector> extends Component< selectors = createInspectorSelectors(); - handleSelectQuery = (queryInfo: QueryInfo) => { + updateSizeMode = (): void => { + if (this.inspectorRef.current) { + const isWideLayout = + this.inspectorRef.current.offsetWidth > RtkQueryInspector.wideLayout; + + if (isWideLayout !== this.state.isWideLayout) { + this.setState({ isWideLayout }); + } + } + }; + + componentDidMount() { + this.updateSizeMode(); + + this.isWideIntervalRef = setInterval(this.updateSizeMode, 200); + } + + componentWillUnmount() { + if (this.isWideIntervalRef) { + clearTimeout(this.isWideIntervalRef as any); + } + } + + handleSelectQuery = (queryInfo: QueryInfo): void => { this.props.dispatch(selectQueryKey(queryInfo) as AnyAction); }; render(): ReactNode { - const { selectorsSource } = this.state; + const { selectorsSource, isWideLayout } = this.state; const { styleUtils: { styling }, } = this.props; @@ -79,11 +106,27 @@ class RtkQueryInspector> extends Component< selectorsSource ); - console.log('inspector', { apiStates, allSortedQueries, selectorsSource }); + const currentQueryInfo = this.selectors.selectorCurrentQueryInfo( + selectorsSource + ); + + console.log('inspector', { + apiStates, + allSortedQueries, + selectorsSource, + currentQueryInfo, + }); return ( -
-
+
+
> extends Component< selectedQueryKey={selectorsSource.monitorState.selectedQueryKey} />
+
); } diff --git a/packages/redux-devtools-rtk-query-inspector-monitor/src/RtkQueryInspectorMonitor.tsx b/packages/redux-devtools-rtk-query-inspector-monitor/src/RtkQueryInspectorMonitor.tsx index 47639055..6cb714c8 100644 --- a/packages/redux-devtools-rtk-query-inspector-monitor/src/RtkQueryInspectorMonitor.tsx +++ b/packages/redux-devtools-rtk-query-inspector-monitor/src/RtkQueryInspectorMonitor.tsx @@ -1,4 +1,4 @@ -import React, { CSSProperties, PureComponent } from 'react'; +import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import * as themes from 'redux-devtools-themes'; import { Action } from 'redux'; @@ -15,18 +15,6 @@ import { StyleUtils, StyleUtilsContext, } from './styles/createStylingFromTheme'; - -const styles: { container: CSSProperties } = { - container: { - fontFamily: 'monaco, Consolas, Lucida Console, monospace', - position: 'relative', - overflowY: 'hidden', - width: '100%', - height: '100%', - minWidth: 300, - }, -}; - interface DefaultProps { select: (state: unknown) => unknown; theme: keyof typeof themes | Base16Theme; @@ -85,20 +73,18 @@ class RtkQueryInspectorMonitor< render() { const { - styleUtils: { base16Theme, styling }, + styleUtils: { base16Theme }, } = this.state; const RtkQueryInspectorAsAny = RtkQueryInspector as any; return ( -
- -
+
); } diff --git a/packages/redux-devtools-rtk-query-inspector-monitor/src/components/QueryPreview.tsx b/packages/redux-devtools-rtk-query-inspector-monitor/src/components/QueryPreview.tsx new file mode 100644 index 00000000..7b0e0245 --- /dev/null +++ b/packages/redux-devtools-rtk-query-inspector-monitor/src/components/QueryPreview.tsx @@ -0,0 +1,96 @@ +import React, { ReactNode } from 'react'; +import JSONTree from 'react-json-tree'; +import { StylingFunction } from 'react-base16-styling'; +import { DATA_TYPE_KEY } from '../monitor-config'; +import { + getJsonTreeTheme, + StyleUtilsContext, +} from '../styles/createStylingFromTheme'; +import { createTreeItemLabelRenderer, getItemString } from '../styles/tree'; +import { QueryInfo } from '../types'; + +export interface QueryPreviewProps { + selectedQueryInfo: QueryInfo | null; + styling: StylingFunction; + isWideLayout: boolean; +} + +export class QueryPreview extends React.PureComponent { + readonly labelRenderer: ReturnType; + + constructor(props: QueryPreviewProps) { + super(props); + + this.labelRenderer = createTreeItemLabelRenderer(this.props.styling); + } + + render(): ReactNode { + const { selectedQueryInfo, isWideLayout } = this.props; + + if (!selectedQueryInfo) { + return ( + + {({ styling }) =>
} + + ); + } + + const { + query: { + endpointName, + fulfilledTimeStamp, + status, + startedTimeStamp, + data, + }, + reducerPath, + } = selectedQueryInfo; + + const startedAt = startedTimeStamp + ? new Date(startedTimeStamp).toISOString() + : '-'; + + const latestFetch = fulfilledTimeStamp + ? new Date(fulfilledTimeStamp).toISOString() + : '-'; + + return ( + + {({ styling, base16Theme, invertTheme }) => { + return ( +
+ +
+
    +
  • {`reducerPath: ${reducerPath ?? '-'}`}
  • +
  • {`endpointName: ${endpointName ?? '-'}`}
  • +
  • {`status: ${status}`}
  • +
  • {`loaded at: ${latestFetch}`}
  • +
  • {`requested at: ${startedAt}`}
  • +
+
+ + getItemString( + styling, + type, + data, + DATA_TYPE_KEY, + isWideLayout + ) + } + hideRoot + /> +
+
+
+ ); + }} +
+ ); + } +} diff --git a/packages/redux-devtools-rtk-query-inspector-monitor/src/monitor-config.ts b/packages/redux-devtools-rtk-query-inspector-monitor/src/monitor-config.ts new file mode 100644 index 00000000..803e1698 --- /dev/null +++ b/packages/redux-devtools-rtk-query-inspector-monitor/src/monitor-config.ts @@ -0,0 +1 @@ +export const DATA_TYPE_KEY = Symbol.for('__serializedType__'); diff --git a/packages/redux-devtools-rtk-query-inspector-monitor/src/selectors.ts b/packages/redux-devtools-rtk-query-inspector-monitor/src/selectors.ts index 62b49247..63152f2b 100644 --- a/packages/redux-devtools-rtk-query-inspector-monitor/src/selectors.ts +++ b/packages/redux-devtools-rtk-query-inspector-monitor/src/selectors.ts @@ -49,6 +49,7 @@ export interface InspectorSelectors { ReturnType >; readonly selectAllSortedQueries: InspectorSelector; + readonly selectorCurrentQueryInfo: InspectorSelector; } export function createInspectorSelectors(): InspectorSelectors { @@ -85,10 +86,30 @@ export function createInspectorSelectors(): InspectorSelectors { } ); + const selectorCurrentQueryInfo = createSelector( + selectAllQueries, + ({ monitorState }: SelectorsSource) => monitorState.selectedQueryKey, + (allQueries, selectedQueryKey) => { + if (!selectedQueryKey) { + return null; + } + + const currentQueryInfo = + allQueries.find( + (query) => + query.queryKey === selectedQueryKey.queryKey && + selectedQueryKey.reducerPath === query.reducerPath + ) || null; + + return currentQueryInfo; + } + ); + return { selectQueryComparator, selectApiStates, selectAllQueries, selectAllSortedQueries, + selectorCurrentQueryInfo, }; } diff --git a/packages/redux-devtools-rtk-query-inspector-monitor/src/styles/createStylingFromTheme.ts b/packages/redux-devtools-rtk-query-inspector-monitor/src/styles/createStylingFromTheme.ts index 8f420872..7ffb0ac0 100644 --- a/packages/redux-devtools-rtk-query-inspector-monitor/src/styles/createStylingFromTheme.ts +++ b/packages/redux-devtools-rtk-query-inspector-monitor/src/styles/createStylingFromTheme.ts @@ -1,5 +1,6 @@ import jss, { StyleSheet } from 'jss'; import preset from 'jss-preset-default'; +import { StylingConfig } from 'react-base16-styling'; import { createStyling, getBase16Theme, @@ -50,7 +51,7 @@ type ColorMap = { const getSheetFromColorMap = (map: ColorMap) => ({ inspector: { display: 'flex', - 'flex-direction': 'column', + flexFlow: 'column nowrap', width: '100%', height: '100%', 'font-family': 'monaco, Consolas, "Lucida Console", monospace', @@ -60,11 +61,30 @@ const getSheetFromColorMap = (map: ColorMap) => ({ 'background-color': map.BACKGROUND_COLOR, color: map.TEXT_COLOR, + + '&[data-wide-layout="1"]': { + flexFlow: 'row nowrap', + }, }, querySectionWrapper: { display: 'flex', - height: '100%', + flex: '0 0 auto', + height: '50%', + width: '100%', + borderColor: map.TAB_BORDER_COLOR, + + '&[data-wide-layout="0"]': { + borderBottomWidth: 1, + borderStyle: 'solid', + }, + + '&[data-wide-layout="1"]': { + height: '100%', + width: '40%', + borderRightWidth: 1, + borderStyle: 'solid', + }, flexFlow: 'column nowrap', '& > :first-child': { flex: '0 0 auto', @@ -230,6 +250,47 @@ const getSheetFromColorMap = (map: ColorMap) => ({ color: map.TEXT_PLACEHOLDER_COLOR, }, }, + + queryPreview: { + flex: '1 1 50%', + display: 'flex', + 'flex-direction': 'column', + 'overflow-y': 'hidden', + '& pre': { + border: 'inherit', + 'border-radius': '3px', + 'line-height': 'inherit', + color: 'inherit', + }, + + 'background-color': map.BACKGROUND_COLOR, + }, + + previewHeader: { + flex: '0 0 30px', + padding: '5px 10px', + 'align-items': 'center', + 'border-bottom-width': '1px', + 'border-bottom-style': 'solid', + + 'background-color': map.HEADER_BACKGROUND_COLOR, + 'border-bottom-color': map.HEADER_BORDER_COLOR, + }, + + treeItemPin: { + 'font-size': '0.7em', + 'padding-left': '5px', + cursor: 'pointer', + '&:hover': { + 'text-decoration': 'underline', + }, + + color: map.PIN_COLOR, + }, + + treeItemKey: { + color: map.TEXT_PLACEHOLDER_COLOR, + }, }); let themeSheet: StyleSheet; @@ -256,6 +317,7 @@ export const createStylingFromTheme = createStyling(getDefaultThemeStyling, { export interface StyleUtils { base16Theme: Base16Theme; styling: StylingFunction; + invertTheme: boolean; } export function createThemeState>( @@ -267,10 +329,31 @@ export function createThemeState>( const theme = props.invertTheme ? invertTheme(props.theme) : props.theme; const styling = createStylingFromTheme(theme); - return { base16Theme, styling }; + return { base16Theme, styling, invertTheme: !!props.invertTheme }; } +const mockStyling = (...args: any[]) => ({ className: '', style: {} }); + export const StyleUtilsContext = createContext({ base16Theme: rtkInspectorTheme, - styling: (...args: any[]) => ({ className: '', style: {} }), + invertTheme: false, + styling: mockStyling, }); + +export function getJsonTreeTheme(base16Theme: Base16Theme): StylingConfig { + return { + extend: base16Theme, + nestedNode: ({ style }, keyPath, nodeType, expanded) => ({ + style: { + ...style, + whiteSpace: expanded ? 'inherit' : 'nowrap', + }, + }), + nestedNodeItemString: ({ style }, keyPath, nodeType, expanded) => ({ + style: { + ...style, + display: expanded ? 'none' : 'inline', + }, + }), + }; +} diff --git a/packages/redux-devtools-rtk-query-inspector-monitor/src/styles/tree.tsx b/packages/redux-devtools-rtk-query-inspector-monitor/src/styles/tree.tsx new file mode 100644 index 00000000..08fafb41 --- /dev/null +++ b/packages/redux-devtools-rtk-query-inspector-monitor/src/styles/tree.tsx @@ -0,0 +1,104 @@ +import React, { ReactNode } from 'react'; +import { StylingFunction } from 'react-base16-styling'; +import { isCollection, isIndexed, isKeyed } from 'immutable'; +import isIterable from '../utils/isIterable'; + +const IS_IMMUTABLE_KEY = '@@__IS_IMMUTABLE__@@'; + +function isImmutable(value: any) { + return isKeyed(value) || isIndexed(value) || isCollection(value); +} + +function getShortTypeString(val: any, diff: boolean | undefined) { + if (diff && Array.isArray(val)) { + val = val[val.length === 2 ? 1 : 0]; + } + + if (isIterable(val) && !isImmutable(val)) { + return '(…)'; + } else if (Array.isArray(val)) { + return val.length > 0 ? '[…]' : '[]'; + } else if (val === null) { + return 'null'; + } else if (val === undefined) { + return 'undef'; + } else if (typeof val === 'object') { + return Object.keys(val).length > 0 ? '{…}' : '{}'; + } else if (typeof val === 'function') { + return 'fn'; + } else if (typeof val === 'string') { + return `"${val.substr(0, 10) + (val.length > 10 ? '…' : '')}"`; + } else if (typeof val === 'symbol') { + return 'symbol'; + } else { + return val; + } +} + +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) as string}` + ) + .concat(keys.length > 3 ? ['…'] : []) + .join(', '); + + return `{ ${str} }`; + } else if (type === 'Array') { + if (!isWideLayout) return data.length ? '[…]' : '[]'; + + const str = data + .slice(0, 4) + .map((val: any) => getShortTypeString(val, isDiff)) + .concat(data.length > 4 ? ['…'] : []) + .join(', '); + + return `[${str as string}]`; + } else { + return type; + } +} + +export function getItemString( + styling: StylingFunction, + type: string, + data: any, + dataTypeKey: string | symbol | undefined, + isWideLayout: boolean, + isDiff?: boolean +): ReactNode { + return ( + + {data[IS_IMMUTABLE_KEY] ? 'Immutable' : ''} + {dataTypeKey && data[dataTypeKey] + ? `${data[dataTypeKey] as string} ` + : ''} + {getText(type, data, isWideLayout, isDiff)} + + ); +} + +export function createTreeItemLabelRenderer(styling: StylingFunction) { + return function labelRenderer( + [key]: (string | number)[], + nodeType: string, + expanded: boolean + ): ReactNode { + return ( + + {key} + {!expanded && ': '} + + ); + }; +} diff --git a/packages/redux-devtools-rtk-query-inspector-monitor/src/utils/isIterable.ts b/packages/redux-devtools-rtk-query-inspector-monitor/src/utils/isIterable.ts new file mode 100644 index 00000000..a7226ad0 --- /dev/null +++ b/packages/redux-devtools-rtk-query-inspector-monitor/src/utils/isIterable.ts @@ -0,0 +1,8 @@ +export default function isIterable(obj: any) { + return ( + obj !== null && + typeof obj === 'object' && + !Array.isArray(obj) && + typeof obj[window.Symbol.iterator] === 'function' + ); +} diff --git a/packages/redux-devtools-rtk-query-inspector-monitor/tsconfig.dev.json b/packages/redux-devtools-rtk-query-inspector-monitor/tsconfig.dev.json index e057814a..852044b7 100644 --- a/packages/redux-devtools-rtk-query-inspector-monitor/tsconfig.dev.json +++ b/packages/redux-devtools-rtk-query-inspector-monitor/tsconfig.dev.json @@ -1,10 +1,9 @@ { - "extends": "../../tsconfig.react.base.json", - "compilerOptions": { - "outDir": "./demo/src/generated-module", - "module": "ES2015", - "strict": false - }, - "include": ["src"], - } - \ No newline at end of file + "extends": "../../tsconfig.react.base.json", + "compilerOptions": { + "outDir": "./demo/src/generated-module", + "module": "ES2015", + "strict": false + }, + "include": ["src"] +}