convert Layout plugin

Summary: _typescript_

Reviewed By: passy

Differential Revision: D17153997

fbshipit-source-id: 308a070b86430a9256beb93b4d3e5f8d5b6c6e52
This commit is contained in:
Daniel Büchele
2019-09-05 02:46:32 -07:00
committed by Facebook Github Bot
parent 705ba8eaa8
commit ef2c6787fa
11 changed files with 238 additions and 184 deletions

View File

@@ -4,4 +4,15 @@
* LICENSE file in the root directory of this source tree.
* @format
*/
export default [];
import {PluginClient, Client, ElementID} from 'flipper';
import {Logger} from 'src/fb-interfaces/Logger';
export default [] as Array<
(
client: PluginClient,
realClient: Client,
selectedNode: ElementID,
logger: Logger,
) => React.ReactNode
>;

View File

@@ -5,36 +5,36 @@
* @format
*/
import type {
import {
ElementID,
Element,
PluginClient,
ElementsInspector,
ElementSearchResultSet,
} from 'flipper';
import {ElementsInspector} from 'flipper';
import {Component} from 'react';
import debounce from 'lodash.debounce';
import type {PersistedState, ElementMap} from './';
import {PersistedState, ElementMap} from './';
import React from 'react';
type GetNodesOptions = {
force?: boolean,
ax?: boolean,
forAccessibilityEvent?: boolean,
force?: boolean;
ax?: boolean;
forAccessibilityEvent?: boolean;
};
type Props = {
ax?: boolean,
client: PluginClient,
showsSidebar: boolean,
inAlignmentMode?: boolean,
selectedElement: ?ElementID,
selectedAXElement: ?ElementID,
onSelect: (ids: ?ElementID) => void,
onDataValueChanged: (path: Array<string>, value: any) => void,
setPersistedState: (state: $Shape<PersistedState>) => void,
persistedState: PersistedState,
searchResults: ?ElementSearchResultSet,
ax?: boolean;
client: PluginClient;
showsSidebar: boolean;
inAlignmentMode?: boolean;
selectedElement: ElementID | null | undefined;
selectedAXElement: ElementID | null | undefined;
onSelect: (ids: ElementID | null | undefined) => void;
onDataValueChanged: (path: Array<string>, value: any) => void;
setPersistedState: (state: Partial<PersistedState>) => void;
persistedState: PersistedState;
searchResults: ElementSearchResultSet | null;
};
export default class Inspector extends Component<Props> {
@@ -76,8 +76,12 @@ export default class Inspector extends Component<Props> {
const elements: Array<Element> = Object.values(
this.props.persistedState.AXelements,
);
return elements.find(i => i?.data?.Accessibility?.['accessibility-focused'])
?.id;
const focusedElement = elements.find(i =>
Boolean(
i.data.Accessibility && i.data.Accessibility['accessibility-focused'],
),
);
return focusedElement ? focusedElement.id : null;
};
getAXContextMenuExtensions = () =>
@@ -106,7 +110,7 @@ export default class Inspector extends Component<Props> {
({
nodes,
}: {
nodes: Array<{id: ElementID, children: Array<ElementID>}>,
nodes: Array<{id: ElementID; children: Array<ElementID>}>;
}) => {
const ids = nodes
.map(n => [n.id, ...(n.children || [])])
@@ -154,7 +158,11 @@ export default class Inspector extends Component<Props> {
selectedElement
];
if (newlySelectedElem) {
this.props.onSelect(newlySelectedElem.extraInfo?.linkedNode);
this.props.onSelect(
newlySelectedElem.extraInfo
? newlySelectedElem.extraInfo.linkedNode
: null,
);
}
} else if (
!ax &&
@@ -166,7 +174,11 @@ export default class Inspector extends Component<Props> {
selectedAXElement
];
if (newlySelectedAXElem) {
this.props.onSelect(newlySelectedAXElem.extraInfo?.linkedNode);
this.props.onSelect(
newlySelectedAXElem.extraInfo
? newlySelectedAXElem.extraInfo.linkedNode
: null,
);
}
}
}
@@ -179,11 +191,13 @@ export default class Inspector extends Component<Props> {
(acc: ElementMap, element: Element) => {
acc[element.id] = {
...element,
expanded: this.elements()[element.id]?.expanded,
expanded: this.elements()[element.id]
? this.elements()[element.id].expanded
: false,
};
return acc;
},
new Map(),
{},
);
this.props.setPersistedState({
[this.props.ax ? 'AXelements' : 'elements']: {
@@ -193,17 +207,19 @@ export default class Inspector extends Component<Props> {
});
}
invalidate(ids: Array<ElementID>): Promise<Array<Element>> {
async invalidate(ids: Array<ElementID>): Promise<Array<Element>> {
if (ids.length === 0) {
return Promise.resolve([]);
}
return this.getNodes(ids, {}).then((elements: Array<Element>) => {
const elements = await this.getNodes(ids, {});
const children = elements
.filter((element: Element) => this.elements()[element.id]?.expanded)
.filter(
(element: Element) =>
this.elements()[element.id] && this.elements()[element.id].expanded,
)
.map((element: Element) => element.children)
.reduce((acc, val) => acc.concat(val), []);
return this.invalidate(children);
});
}
updateElement(id: ElementID, data: Object) {
@@ -225,7 +241,7 @@ export default class Inspector extends Component<Props> {
// element has no children so we're as deep as we can be
return;
}
return this.getChildren(element.id, {}).then((elements: Array<Element>) => {
return this.getChildren(element.id, {}).then(() => {
if (element.children.length >= 2) {
// element has two or more children so we can stop expanding
return;
@@ -245,32 +261,32 @@ export default class Inspector extends Component<Props> {
return this.getNodes(this.elements()[id].children, options);
}
getNodes(
async getNodes(
ids: Array<ElementID> = [],
options: GetNodesOptions,
): Promise<Array<Element>> {
const {forAccessibilityEvent} = options;
if (ids.length > 0) {
return this.props.client
.call(this.call().GET_NODES, {
const {forAccessibilityEvent} = options;
const {
elements,
}: {elements: Array<Element>} = await this.props.client.call(
this.call().GET_NODES,
{
ids,
forAccessibilityEvent,
selected: false,
})
.then(({elements}) => {
},
);
elements.forEach(e => this.updateElement(e.id, e));
return elements;
});
} else {
return Promise.resolve([]);
return [];
}
}
getAndExpandPath(path: Array<ElementID>) {
return Promise.all(path.map(id => this.getChildren(id, {}))).then(() => {
async getAndExpandPath(path: Array<ElementID>) {
await Promise.all(path.map(id => this.getChildren(id, {})));
this.onElementSelected(path[path.length - 1]);
});
}
onElementSelected = debounce((selectedKey: ElementID) => {
@@ -278,7 +294,7 @@ export default class Inspector extends Component<Props> {
this.props.onSelect(selectedKey);
});
onElementHovered = debounce((key: ?ElementID) =>
onElementHovered = debounce((key: ElementID | null | undefined) =>
this.props.client.call(this.call().SET_HIGHLIGHTED, {
id: key,
isAlignmentMode: this.props.inAlignmentMode,

View File

@@ -5,22 +5,21 @@
* @format
*/
import type {Element} from 'flipper';
import type {PluginClient} from 'flipper';
import type Client from '../../Client.tsx';
import type {Logger} from '../../fb-interfaces/Logger.tsx';
import {
ManagedDataInspector,
Panel,
FlexCenter,
styled,
colors,
PluginClient,
SidebarExtensions,
Element,
} from 'flipper';
import Client from '../../Client';
import {Logger} from '../../fb-interfaces/Logger';
import {Component} from 'react';
const deepEqual = require('deep-equal');
import deepEqual from 'deep-equal';
import React from 'react';
const NoData = styled(FlexCenter)({
fontSize: 18,
@@ -30,10 +29,10 @@ const NoData = styled(FlexCenter)({
type OnValueChanged = (path: Array<string>, val: any) => void;
type InspectorSidebarSectionProps = {
data: any,
id: string,
onValueChanged: ?OnValueChanged,
tooltips?: Object,
data: any;
id: string;
onValueChanged: OnValueChanged | null;
tooltips?: Object;
};
class InspectorSidebarSection extends Component<InspectorSidebarSectionProps> {
@@ -51,7 +50,7 @@ class InspectorSidebarSection extends Component<InspectorSidebarSectionProps> {
);
}
extractValue = (val: any, depth: number) => {
extractValue = (val: any, _depth: number) => {
if (val && val.__type__) {
return {
mutable: Boolean(val.__mutable__),
@@ -84,14 +83,14 @@ class InspectorSidebarSection extends Component<InspectorSidebarSectionProps> {
}
}
type Props = {|
element: ?Element,
tooltips?: Object,
onValueChanged: ?OnValueChanged,
client: PluginClient,
realClient: Client,
logger: Logger,
|};
type Props = {
element: Element | null;
tooltips?: Object;
onValueChanged: OnValueChanged | null;
client: PluginClient;
realClient: Client;
logger: Logger;
};
export default class Sidebar extends Component<Props> {
render() {
@@ -115,12 +114,13 @@ export default class Sidebar extends Component<Props> {
for (const key in element.data) {
if (key === 'Extra Sections') {
for (const extraSection in element.data[key]) {
let data = element.data[key][extraSection];
const section = element.data[key][extraSection];
let data = {};
// data might be sent as stringified JSON, we want to parse it for a nicer persentation.
if (typeof data === 'string') {
if (typeof section === 'string') {
try {
data = JSON.parse(data);
data = JSON.parse(section);
} catch (e) {
// data was not a valid JSON, type is required to be an object
console.error(
@@ -128,6 +128,8 @@ export default class Sidebar extends Component<Props> {
);
data = {};
}
} else {
data = section;
}
sections.push(
<InspectorSidebarSection

View File

@@ -5,15 +5,18 @@
* @format
*/
import type {Element, ElementID} from 'flipper';
import type {PersistedState} from './index';
import type {SearchResultTree} from './Search';
// $FlowFixMe
import {Element} from 'flipper';
import {PersistedState} from './index';
import {SearchResultTree} from './Search';
import cloneDeep from 'lodash.clonedeep';
const propsForPersistedState = (
AXMode: boolean,
): {ROOT: string, ELEMENTS: string, ELEMENT: string} => {
): {
ROOT: 'rootAXElement' | 'rootElement';
ELEMENTS: 'AXelements' | 'elements';
ELEMENT: 'axElement' | 'element';
} => {
return {
ROOT: AXMode ? 'rootAXElement' : 'rootElement',
ELEMENTS: AXMode ? 'AXelements' : 'elements',
@@ -25,14 +28,14 @@ function constructSearchResultTree(
node: Element,
isMatch: boolean,
children: Array<SearchResultTree>,
AXMode: boolean,
AXNode: ?Element,
_AXMode: boolean,
AXNode: Element | null,
): SearchResultTree {
const searchResult = {
id: node.id,
isMatch,
hasChildren: children.length > 0,
children: children.length > 0 ? children : null,
children: children.length > 0 ? children : [],
element: node,
axElement: AXNode,
};
@@ -49,7 +52,7 @@ export function searchNodes(
query: string,
AXMode: boolean,
state: PersistedState,
): ?SearchResultTree {
): SearchResultTree | null {
// Even if the axMode is true, we will have to search the normal elements too.
// The AXEelements will automatically populated in constructSearchResultTree
const elements = state[propsForPersistedState(false).ELEMENTS];
@@ -83,20 +86,19 @@ class ProxyArchiveClient {
this.persistedState = cloneDeep(persistedState);
}
persistedState: PersistedState;
subscribe(method: string, callback: (params: any) => void): void {
subscribe(_method: string, _callback: (params: any) => void): void {
return;
}
supportsMethod(method: string): Promise<boolean> {
supportsMethod(_method: string): Promise<boolean> {
return Promise.resolve(false);
}
send(method: string, params?: Object): void {
send(_method: string, _params?: Object): void {
return;
}
call(method: string, params?: Object): Promise<any> {
const paramaters = params;
call(method: string, paramaters?: {[key: string]: any}): Promise<any> {
switch (method) {
case 'getRoot': {
const {rootElement} = this.persistedState;
@@ -118,7 +120,7 @@ class ProxyArchiveClient {
}
const {ids} = paramaters;
const arr: Array<Element> = [];
for (const id: ElementID of ids) {
for (const id of ids) {
arr.push(this.persistedState.elements[id]);
}
return Promise.resolve({elements: arr});
@@ -129,7 +131,7 @@ class ProxyArchiveClient {
}
const {ids} = paramaters;
const arr: Array<Element> = [];
for (const id: ElementID of ids) {
for (const id of ids) {
arr.push(this.persistedState.AXelements[id]);
}
return Promise.resolve({elements: arr});
@@ -148,7 +150,7 @@ class ProxyArchiveClient {
new Error('query is not passed as a params to getSearchResults'),
);
}
let element = {};
let element: Element;
if (axEnabled) {
if (!rootAXElement) {
return Promise.reject(new Error('rootAXElement is undefined'));

View File

@@ -5,10 +5,11 @@
* @format
*/
import type {PluginClient, ElementSearchResultSet, Element} from 'flipper';
import type {PersistedState, ElementMap} from './';
import {PersistedState, ElementMap} from './';
import {
PluginClient,
ElementSearchResultSet,
Element,
SearchInput,
SearchBox,
SearchIcon,
@@ -17,28 +18,29 @@ import {
colors,
} from 'flipper';
import {Component} from 'react';
import React from 'react';
export type SearchResultTree = {|
id: string,
isMatch: boolean,
hasChildren: boolean,
children: ?Array<SearchResultTree>,
element: Element,
axElement: ?Element, // Not supported in iOS
|};
export type SearchResultTree = {
id: string;
isMatch: boolean;
hasChildren: boolean;
children: Array<SearchResultTree>;
element: Element;
axElement: Element | null; // Not supported in iOS
};
type Props = {
client: PluginClient,
inAXMode: boolean,
onSearchResults: (searchResults: ElementSearchResultSet) => void,
setPersistedState: (state: $Shape<PersistedState>) => void,
persistedState: PersistedState,
initialQuery: ?string,
client: PluginClient;
inAXMode: boolean;
onSearchResults: (searchResults: ElementSearchResultSet) => void;
setPersistedState: (state: Partial<PersistedState>) => void;
persistedState: PersistedState;
initialQuery: string | null;
};
type State = {
value: string,
outstandingSearchQuery: ?string,
value: string;
outstandingSearchQuery: string | null;
};
const LoadingSpinner = styled(LoadingIndicator)({
@@ -53,16 +55,18 @@ export default class Search extends Component<Props, State> {
outstandingSearchQuery: null,
};
timer: TimeoutID;
timer: NodeJS.Timeout | undefined;
onChange = (e: SyntheticInputEvent<>) => {
onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (this.timer) {
clearTimeout(this.timer);
}
const {value} = e.target;
this.setState({value});
this.timer = setTimeout(() => this.performSearch(value), 200);
};
onKeyDown = (e: SyntheticKeyboardEvent<>) => {
onKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Enter') {
this.performSearch(this.state.value);
}
@@ -73,7 +77,9 @@ export default class Search extends Component<Props, State> {
const queryString = this.props.initialQuery
? this.props.initialQuery
: '';
if (this.timer) {
clearTimeout(this.timer);
}
this.timer = setTimeout(() => this.performSearch(queryString), 200);
}
}
@@ -102,8 +108,8 @@ export default class Search extends Component<Props, State> {
results,
query,
}: {
results: ?SearchResultTree,
query: string,
results: SearchResultTree | null;
query: string;
},
axMode: boolean,
) {
@@ -159,13 +165,14 @@ export default class Search extends Component<Props, State> {
}
getElementsFromSearchResultTree(
tree: ?SearchResultTree,
tree: SearchResultTree | null,
): Array<SearchResultTree> {
if (!tree) {
return [];
}
let elements = [
{
children: [] as Array<SearchResultTree>,
id: tree.id,
isMatch: tree.isMatch,
hasChildren: Boolean(tree.children),

View File

@@ -6,13 +6,14 @@
*/
import {Glyph, styled, colors} from 'flipper';
import React from 'react';
type Props = {|
title: string,
icon: string,
active: boolean,
onClick: () => void,
|};
type Props = {
title: string;
icon: string;
active: boolean;
onClick: () => void;
};
const ToolbarIcon = styled('div')({
marginRight: 9,

View File

@@ -9,9 +9,9 @@ import {
default as ProxyArchiveClient,
searchNodes,
} from '../ProxyArchiveClient';
import type {PersistedState} from '../index';
import type {ElementID, Element} from 'flipper';
import type {SearchResultTree} from '../Search';
import {PersistedState, ElementMap} from '../index';
import {ElementID, Element} from 'flipper';
import {SearchResultTree} from '../Search';
function constructElement(
id: string,
@@ -49,7 +49,7 @@ function constructPersistedState(axMode: boolean): PersistedState {
let state = constructPersistedState(false);
function populateChildren(state: PersistedState, axMode: boolean) {
const elements = {};
const elements: ElementMap = {};
elements['root'] = constructElement('root', 'root view', [
'child0',
'child1',
@@ -95,7 +95,7 @@ beforeEach(() => {
});
test('test the searchNode for root in axMode false', async () => {
const searchResult: ?SearchResultTree = await searchNodes(
const searchResult: SearchResultTree | null = await searchNodes(
state.elements['root'],
'root',
false,
@@ -106,7 +106,7 @@ test('test the searchNode for root in axMode false', async () => {
id: 'root',
isMatch: true,
hasChildren: false,
children: null,
children: [],
element: state.elements['root'],
axElement: null,
});
@@ -115,7 +115,7 @@ test('test the searchNode for root in axMode false', async () => {
test('test the searchNode for root in axMode true', async () => {
state = constructPersistedState(true);
populateChildren(state, true);
const searchResult: ?SearchResultTree = await searchNodes(
const searchResult: SearchResultTree | null = await searchNodes(
state.AXelements['root'],
'RoOT',
true,
@@ -126,14 +126,14 @@ test('test the searchNode for root in axMode true', async () => {
id: 'root',
isMatch: true,
hasChildren: false,
children: null,
children: [],
element: state.AXelements['root'], // Even though AXElement exists, normal element will exist too
axElement: state.AXelements['root'],
});
});
test('test the searchNode which matches just one child', async () => {
const searchResult: ?SearchResultTree = await searchNodes(
const searchResult: SearchResultTree | null = await searchNodes(
state.elements['root'],
'child0_child0',
false,
@@ -154,7 +154,7 @@ test('test the searchNode which matches just one child', async () => {
id: 'child0_child0',
isMatch: true,
hasChildren: false,
children: null,
children: [],
element: state.elements['child0_child0'],
axElement: null,
},
@@ -169,7 +169,7 @@ test('test the searchNode which matches just one child', async () => {
});
test('test the searchNode for which matches multiple child', async () => {
const searchResult: ?SearchResultTree = await searchNodes(
const searchResult: SearchResultTree | null = await searchNodes(
state.elements['root'],
'child0',
false,
@@ -190,7 +190,7 @@ test('test the searchNode for which matches multiple child', async () => {
id: 'child0_child0',
isMatch: true,
hasChildren: false,
children: null,
children: [],
element: state.elements['child0_child0'],
axElement: null,
},
@@ -198,7 +198,7 @@ test('test the searchNode for which matches multiple child', async () => {
id: 'child0_child1',
isMatch: true,
hasChildren: false,
children: null,
children: [],
element: state.elements['child0_child1'],
axElement: null,
},
@@ -215,7 +215,7 @@ test('test the searchNode for which matches multiple child', async () => {
id: 'child1_child0',
isMatch: true,
hasChildren: false,
children: null,
children: [],
element: state.elements['child1_child0'],
axElement: null,
},
@@ -231,7 +231,7 @@ test('test the searchNode for which matches multiple child', async () => {
});
test('test the searchNode, it should not be case sensitive', async () => {
const searchResult: ?SearchResultTree = await searchNodes(
const searchResult: SearchResultTree | null = await searchNodes(
state.elements['root'],
'ChIlD0',
false,
@@ -252,7 +252,7 @@ test('test the searchNode, it should not be case sensitive', async () => {
id: 'child0_child0',
isMatch: true,
hasChildren: false,
children: null,
children: [],
element: state.elements['child0_child0'],
axElement: null,
},
@@ -260,7 +260,7 @@ test('test the searchNode, it should not be case sensitive', async () => {
id: 'child0_child1',
isMatch: true,
hasChildren: false,
children: null,
children: [],
element: state.elements['child0_child1'],
axElement: null,
},
@@ -277,7 +277,7 @@ test('test the searchNode, it should not be case sensitive', async () => {
id: 'child1_child0',
isMatch: true,
hasChildren: false,
children: null,
children: [],
element: state.elements['child1_child0'],
axElement: null,
},
@@ -293,7 +293,7 @@ test('test the searchNode, it should not be case sensitive', async () => {
});
test('test the searchNode for non existent query', async () => {
const searchResult: ?SearchResultTree = await searchNodes(
const searchResult: SearchResultTree | null = await searchNodes(
state.elements['root'],
'Unknown query',
false,

View File

@@ -5,20 +5,16 @@
* @format
*/
import type {
import {
ElementID,
Element,
ElementSearchResultSet,
MiddlewareAPI,
PluginClient,
} from 'flipper';
import {
FlexColumn,
FlexRow,
FlipperPlugin,
Toolbar,
Sidebar,
DetailSidebar,
VerticalRule,
Button,
@@ -29,37 +25,42 @@ import ToolbarIcon from './ToolbarIcon';
import InspectorSidebar from './InspectorSidebar';
import Search from './Search';
import ProxyArchiveClient from './ProxyArchiveClient';
import React from 'react';
type State = {|
init: boolean,
inTargetMode: boolean,
inAXMode: boolean,
inAlignmentMode: boolean,
selectedElement: ?ElementID,
selectedAXElement: ?ElementID,
searchResults: ?ElementSearchResultSet,
|};
type State = {
init: boolean;
inTargetMode: boolean;
inAXMode: boolean;
inAlignmentMode: boolean;
selectedElement: ElementID | null | undefined;
selectedAXElement: ElementID | null | undefined;
searchResults: ElementSearchResultSet | null;
};
export type ElementMap = {[key: ElementID]: Element};
export type ElementMap = {[key: string]: Element};
export type PersistedState = {|
rootElement: ?ElementID,
rootAXElement: ?ElementID,
elements: ElementMap,
AXelements: ElementMap,
|};
export type PersistedState = {
rootElement: ElementID | null;
rootAXElement: ElementID | null;
elements: ElementMap;
AXelements: ElementMap;
};
export default class Layout extends FlipperPlugin<State, void, PersistedState> {
static exportPersistedState = (
callClient: (string, ?Object) => Promise<Object>,
persistedState: ?PersistedState,
store: ?MiddlewareAPI,
): Promise<?PersistedState> => {
const defaultPromise = Promise.resolve(persistedState);
export default class Layout extends FlipperPlugin<State, any, PersistedState> {
static exportPersistedState = async (
callClient: (
method: 'getAllNodes',
) => Promise<{
allNodes: PersistedState;
}>,
persistedState: PersistedState | undefined,
store: MiddlewareAPI | undefined,
): Promise<PersistedState | undefined> => {
if (!store) {
return defaultPromise;
return persistedState;
}
return callClient('getAllNodes').then(({allNodes}) => allNodes);
const {allNodes} = await callClient('getAllNodes');
return allNodes;
};
static defaultPersistedState = {
@@ -69,7 +70,7 @@ export default class Layout extends FlipperPlugin<State, void, PersistedState> {
AXelements: {},
};
state = {
state: State = {
init: false,
inTargetMode: false,
inAXMode: false,
@@ -154,13 +155,12 @@ export default class Layout extends FlipperPlugin<State, void, PersistedState> {
searchResults: this.state.searchResults,
};
let element;
if (this.state.inAXMode && this.state.selectedAXElement) {
element = this.props.persistedState.AXelements[
this.state.selectedAXElement
];
} else if (this.state.selectedElement) {
element = this.props.persistedState.elements[this.state.selectedElement];
let element: Element | null = null;
const {selectedAXElement, selectedElement, inAXMode} = this.state;
if (inAXMode && selectedAXElement) {
element = this.props.persistedState.AXelements[selectedAXElement];
} else if (selectedElement) {
element = this.props.persistedState.elements[selectedElement];
}
const inspector = (
@@ -247,7 +247,7 @@ export default class Layout extends FlipperPlugin<State, void, PersistedState> {
compact={true}
style={{marginTop: 8, marginRight: 12}}
onClick={() => {
this.props.selectPlugin('YogaPerformance', element.id);
this.props.selectPlugin('YogaPerformance', element!.id);
}}>
Analyze Yoga Performance
</Button>

View File

@@ -1,7 +1,7 @@
{
"name": "Inspector",
"version": "1.0.0",
"main": "index.js",
"main": "index.tsx",
"license": "MIT",
"dependencies": {
"deep-equal": "^1.0.1",
@@ -13,5 +13,8 @@
"bugs": {
"email": "oncall+flipper@xmail.facebook.com",
"url": "https://fb.workplace.com/groups/230455004101832/"
},
"devDependencies": {
"@types/lodash.clonedeep": "^4.5.6"
}
}

View File

@@ -2,6 +2,18 @@
# yarn lockfile v1
"@types/lodash.clonedeep@^4.5.6":
version "4.5.6"
resolved "https://registry.yarnpkg.com/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.6.tgz#3b6c40a0affe0799a2ce823b440a6cf33571d32b"
integrity sha512-cE1jYr2dEg1wBImvXlNtp0xDoS79rfEdGozQVgliDZj1uERH4k+rmEMTudP9b4VQ8O6nRb5gPqft0QzEQGMQgA==
dependencies:
"@types/lodash" "*"
"@types/lodash@*":
version "4.14.138"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.138.tgz#34f52640d7358230308344e579c15b378d91989e"
integrity sha512-A4uJgHz4hakwNBdHNPdxOTkYmXNgmUAKLbXZ7PKGslgeV0Mb8P3BlbYfPovExek1qnod4pDfRbxuzcVs3dlFLg==
deep-equal@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5"

View File

@@ -1,7 +1,7 @@
{
"compilerOptions": {
"module": "system",
"lib": ["es7", "dom"],
"lib": ["es7", "dom", "es2017"],
"target": "es6",
"removeComments": true,
"preserveConstEnums": true,