feat: support close all connections
for https://github.com/haishanh/yacd/issues/338
This commit is contained in:
parent
19ecf435de
commit
8b5ecb3f18
16 changed files with 229 additions and 40 deletions
|
@ -10,7 +10,8 @@ const presets = [
|
||||||
corejs: 3
|
corejs: 3
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
'@babel/preset-react'
|
'@babel/preset-react',
|
||||||
|
'@babel/preset-flow'
|
||||||
];
|
];
|
||||||
|
|
||||||
const plugins = [
|
const plugins = [
|
||||||
|
|
|
@ -67,6 +67,7 @@
|
||||||
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
|
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
|
||||||
"@babel/plugin-transform-runtime": "^7.6.2",
|
"@babel/plugin-transform-runtime": "^7.6.2",
|
||||||
"@babel/preset-env": "^7.7.1",
|
"@babel/preset-env": "^7.7.1",
|
||||||
|
"@babel/preset-flow": "^7.7.4",
|
||||||
"@babel/preset-react": "^7.7.0",
|
"@babel/preset-react": "^7.7.0",
|
||||||
"autoprefixer": "^9.7.1",
|
"autoprefixer": "^9.7.1",
|
||||||
"babel-eslint": "^10.0.3",
|
"babel-eslint": "^10.0.3",
|
||||||
|
|
|
@ -1,10 +1,39 @@
|
||||||
|
import { getURLAndInit } from 'm/request-helper';
|
||||||
|
|
||||||
const endpoint = '/connections';
|
const endpoint = '/connections';
|
||||||
|
|
||||||
let fetched = false;
|
let fetched = false;
|
||||||
let subscribers = [];
|
let subscribers = [];
|
||||||
|
|
||||||
|
// see also https://github.com/Dreamacro/clash/blob/dev/constant/metadata.go#L41
|
||||||
|
type UUID = string;
|
||||||
|
type ConnectionItem = {
|
||||||
|
id: UUID,
|
||||||
|
metadata: {
|
||||||
|
network: 'tcp' | 'udp',
|
||||||
|
type: 'HTTP' | 'HTTP Connect' | 'Socks5' | 'Redir' | 'Unknown',
|
||||||
|
sourceIP: string,
|
||||||
|
destinationIP: string,
|
||||||
|
sourcePort: string,
|
||||||
|
destinationPort: string,
|
||||||
|
host: string
|
||||||
|
},
|
||||||
|
upload: number,
|
||||||
|
download: number,
|
||||||
|
// e.g. "2019-11-30T22:48:13.416668+08:00",
|
||||||
|
start: string,
|
||||||
|
chains: Array<string>,
|
||||||
|
// e.g. 'Match', 'DomainKeyword'
|
||||||
|
rule: string
|
||||||
|
};
|
||||||
|
type ConnectionsData = {
|
||||||
|
downloadTotal: number,
|
||||||
|
uploadTotal: number,
|
||||||
|
connections: Array<ConnectionItem>
|
||||||
|
};
|
||||||
|
|
||||||
function appendData(s) {
|
function appendData(s) {
|
||||||
let o;
|
let o: ConnectionsData;
|
||||||
try {
|
try {
|
||||||
o = JSON.parse(s);
|
o = JSON.parse(s);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -48,4 +77,9 @@ function subscribe(listener) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export { fetchData };
|
async function closeAllConnections(apiConfig) {
|
||||||
|
const { url, init } = getURLAndInit(apiConfig);
|
||||||
|
return await fetch(url + endpoint, { ...init, method: 'DELETE' });
|
||||||
|
}
|
||||||
|
|
||||||
|
export { fetchData, closeAllConnections };
|
||||||
|
|
|
@ -1,20 +1,29 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import s0 from 'c/Button.module.css';
|
import s0 from 'c/Button.module.css';
|
||||||
const noop = () => {};
|
const noop = () => {};
|
||||||
|
|
||||||
const Button = React.memo(function Button({ label, onClick = noop }) {
|
const { memo, forwardRef } = React;
|
||||||
|
|
||||||
|
function Button({ children, label, onClick = noop }, ref) {
|
||||||
return (
|
return (
|
||||||
<button className={s0.btn} onClick={onClick}>
|
<button className={s0.btn} ref={ref} onClick={onClick}>
|
||||||
{label}
|
{children || label}
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
|
||||||
Button.propTypes = {
|
function WithIcon({ text, icon, onClick = noop }, ref) {
|
||||||
label: PropTypes.string.isRequired,
|
return (
|
||||||
onClick: PropTypes.func
|
<button className={s0.btn} ref={ref} onClick={onClick}>
|
||||||
};
|
<div className={s0.withIconWrapper}>
|
||||||
|
{icon}
|
||||||
|
<span className={s0.txt}>{text}</span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default Button;
|
export const ButtonWithIcon = memo(forwardRef(WithIcon));
|
||||||
|
|
||||||
|
export default memo(forwardRef(Button));
|
||||||
|
|
|
@ -25,3 +25,11 @@
|
||||||
padding: 6px 12px;
|
padding: 6px 12px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.withIconWrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.txt {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -34,6 +34,13 @@ function renderCell(cell, now) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tableState = {
|
||||||
|
sortBy: [
|
||||||
|
// maintain a more stable order
|
||||||
|
{ id: 'start', desc: true }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
function Table({ data }) {
|
function Table({ data }) {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const {
|
const {
|
||||||
|
@ -45,7 +52,8 @@ function Table({ data }) {
|
||||||
} = useTable(
|
} = useTable(
|
||||||
{
|
{
|
||||||
columns,
|
columns,
|
||||||
data
|
data,
|
||||||
|
initialState: tableState
|
||||||
},
|
},
|
||||||
useSortBy
|
useSortBy
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,12 +4,15 @@ import ConnectionTable from 'c/ConnectionTable';
|
||||||
import useRemainingViewPortHeight from '../hooks/useRemainingViewPortHeight';
|
import useRemainingViewPortHeight from '../hooks/useRemainingViewPortHeight';
|
||||||
import { useStoreState } from 'm/store';
|
import { useStoreState } from 'm/store';
|
||||||
import { getClashAPIConfig } from 'd/app';
|
import { getClashAPIConfig } from 'd/app';
|
||||||
|
import { X as IconClose } from 'react-feather';
|
||||||
import SvgYacd from './SvgYacd';
|
import SvgYacd from './SvgYacd';
|
||||||
|
import { ButtonWithIcon } from './Button';
|
||||||
|
import ModalCloseAllConnections from './ModalCloseAllConnections';
|
||||||
import * as connAPI from '../api/connections';
|
import * as connAPI from '../api/connections';
|
||||||
|
|
||||||
import s from './Connections.module.css';
|
import s from './Connections.module.css';
|
||||||
|
|
||||||
const { useEffect, useState, useRef } = React;
|
const { useEffect, useState, useRef, useCallback, useMemo } = React;
|
||||||
|
|
||||||
const paddingBottom = 30;
|
const paddingBottom = 30;
|
||||||
|
|
||||||
|
@ -31,6 +34,17 @@ function Conn() {
|
||||||
const [refContainer, containerHeight] = useRemainingViewPortHeight();
|
const [refContainer, containerHeight] = useRemainingViewPortHeight();
|
||||||
const config = useStoreState(getClashAPIConfig);
|
const config = useStoreState(getClashAPIConfig);
|
||||||
const [conns, setConns] = useState([]);
|
const [conns, setConns] = useState([]);
|
||||||
|
const [isCloseAllModalOpen, setIsCloseAllModalOpen] = useState(false);
|
||||||
|
const openCloseAllModal = useCallback(() => setIsCloseAllModalOpen(true), []);
|
||||||
|
const closeCloseAllModal = useCallback(
|
||||||
|
() => setIsCloseAllModalOpen(false),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
const closeAllConnections = useCallback(() => {
|
||||||
|
connAPI.closeAllConnections(config);
|
||||||
|
closeCloseAllModal();
|
||||||
|
}, [config, closeCloseAllModal]);
|
||||||
|
const iconClose = useMemo(() => <IconClose width={16} />, []);
|
||||||
const prevConnsRef = useRef(conns);
|
const prevConnsRef = useRef(conns);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function read({ connections }) {
|
function read({ connections }) {
|
||||||
|
@ -65,6 +79,18 @@ function Conn() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="fabgrp">
|
||||||
|
<ButtonWithIcon
|
||||||
|
text="Close"
|
||||||
|
icon={iconClose}
|
||||||
|
onClick={openCloseAllModal}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<ModalCloseAllConnections
|
||||||
|
isOpen={isCloseAllModalOpen}
|
||||||
|
primaryButtonOnTap={closeAllConnections}
|
||||||
|
onRequestClose={closeCloseAllModal}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
44
src/components/ModalCloseAllConnections.js
Normal file
44
src/components/ModalCloseAllConnections.js
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import Modal from 'react-modal';
|
||||||
|
import Button from './Button';
|
||||||
|
import cx from 'classnames';
|
||||||
|
|
||||||
|
import modalStyle from './Modal.module.css';
|
||||||
|
import s from './ModalCloseAllConnections.module.css';
|
||||||
|
|
||||||
|
const { useRef, useCallback, useMemo } = React;
|
||||||
|
|
||||||
|
export default function Comp({ isOpen, onRequestClose, primaryButtonOnTap }) {
|
||||||
|
const primaryButtonRef = useRef(null);
|
||||||
|
const onAfterOpen = useCallback(() => {
|
||||||
|
primaryButtonRef.current.focus();
|
||||||
|
}, []);
|
||||||
|
const className = useMemo(
|
||||||
|
() => ({
|
||||||
|
base: cx(modalStyle.content, s.cnt),
|
||||||
|
afterOpen: s.afterOpen,
|
||||||
|
beforeClose: ''
|
||||||
|
}),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isOpen={isOpen}
|
||||||
|
onRequestClose={onRequestClose}
|
||||||
|
onAfterOpen={onAfterOpen}
|
||||||
|
className={className}
|
||||||
|
overlayClassName={cx(modalStyle.overlay, s.overlay)}
|
||||||
|
>
|
||||||
|
<p>Are you sure you want to close all connections?</p>
|
||||||
|
<div className={s.btngrp}>
|
||||||
|
<Button onClick={primaryButtonOnTap} ref={primaryButtonRef}>
|
||||||
|
I'm sure
|
||||||
|
</Button>
|
||||||
|
{/* im lazy :) */}
|
||||||
|
<div style={{ width: 20 }} />
|
||||||
|
<Button onClick={onRequestClose}>No</Button>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
23
src/components/ModalCloseAllConnections.module.css
Normal file
23
src/components/ModalCloseAllConnections.module.css
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
.overlay {
|
||||||
|
background-color: rgba(0, 0, 0, 0.6);
|
||||||
|
}
|
||||||
|
.cnt {
|
||||||
|
background-color: var(--bg-modal);
|
||||||
|
color: var(--color-text);
|
||||||
|
max-width: 300px;
|
||||||
|
line-height: 1.4;
|
||||||
|
transform: translate(-50%, -50%) scale(1.5);
|
||||||
|
opacity: 0.6;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
.afterOpen {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translate(-50%, -50%) scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btngrp {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
|
@ -1,9 +1,10 @@
|
||||||
import React, { useEffect } from 'react';
|
import React from 'react';
|
||||||
import { useActions, useStoreState } from 'm/store';
|
import { useActions, useStoreState } from 'm/store';
|
||||||
|
|
||||||
import ContentHeader from 'c/ContentHeader';
|
import ContentHeader from 'c/ContentHeader';
|
||||||
import ProxyGroup from 'c/ProxyGroup';
|
import ProxyGroup from 'c/ProxyGroup';
|
||||||
import Button from 'c/Button';
|
import { ButtonWithIcon } from 'c/Button';
|
||||||
|
import { Zap } from 'react-feather';
|
||||||
|
|
||||||
import s0 from 'c/Proxies.module.css';
|
import s0 from 'c/Proxies.module.css';
|
||||||
|
|
||||||
|
@ -14,6 +15,8 @@ import {
|
||||||
requestDelayAll
|
requestDelayAll
|
||||||
} from 'd/proxies';
|
} from 'd/proxies';
|
||||||
|
|
||||||
|
const { useEffect, useMemo } = React;
|
||||||
|
|
||||||
const mapStateToProps = s => ({
|
const mapStateToProps = s => ({
|
||||||
proxies: getProxies(s),
|
proxies: getProxies(s),
|
||||||
groupNames: getProxyGroupNames(s)
|
groupNames: getProxyGroupNames(s)
|
||||||
|
@ -33,13 +36,19 @@ export default function Proxies() {
|
||||||
})();
|
})();
|
||||||
}, [fetchProxies, requestDelayAll]);
|
}, [fetchProxies, requestDelayAll]);
|
||||||
const { groupNames } = useStoreState(mapStateToProps);
|
const { groupNames } = useStoreState(mapStateToProps);
|
||||||
|
const icon = useMemo(() => <Zap width={16} />, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ContentHeader title="Proxies" />
|
<ContentHeader title="Proxies" />
|
||||||
<div className={s0.body}>
|
<div className={s0.body}>
|
||||||
<div className={s0.fabgrp}>
|
<div className="fabgrp">
|
||||||
<Button label="Test Latency" onClick={requestDelayAll} />
|
<ButtonWithIcon
|
||||||
|
text="Test Latency"
|
||||||
|
icon={icon}
|
||||||
|
onClick={requestDelayAll}
|
||||||
|
/>
|
||||||
|
{/* <Button onClick={requestDelayAll}>Test Latency</Button> */}
|
||||||
</div>
|
</div>
|
||||||
{groupNames.map(groupName => {
|
{groupNames.map(groupName => {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -8,10 +8,3 @@
|
||||||
padding: 10px 40px;
|
padding: 10px 40px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.fabgrp {
|
|
||||||
position: fixed;
|
|
||||||
z-index: 1;
|
|
||||||
right: 20px;
|
|
||||||
bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
|
@ -92,6 +92,7 @@ body.dark {
|
||||||
--color-btn-fg: #bebebe;
|
--color-btn-fg: #bebebe;
|
||||||
--color-bg-proxy-selected: #303030;
|
--color-bg-proxy-selected: #303030;
|
||||||
--color-row-odd: #282828;
|
--color-row-odd: #282828;
|
||||||
|
--bg-modal: #1f1f20;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.light {
|
body.light {
|
||||||
|
@ -109,4 +110,12 @@ body.light {
|
||||||
--color-btn-fg: #101010;
|
--color-btn-fg: #101010;
|
||||||
--color-bg-proxy-selected: #cfcfcf;
|
--color-bg-proxy-selected: #cfcfcf;
|
||||||
--color-row-odd: #f5f5f5;
|
--color-row-odd: #f5f5f5;
|
||||||
|
--bg-modal: #fbfbfb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO remove fabgrp in component css files */
|
||||||
|
.fabgrp {
|
||||||
|
position: fixed;
|
||||||
|
right: 20px;
|
||||||
|
bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import React, { memo, useEffect } from 'react';
|
import React from 'react';
|
||||||
import { useActions, useStoreState } from 'm/store';
|
import { useActions, useStoreState } from 'm/store';
|
||||||
import Button from 'c/Button';
|
import { ButtonWithIcon } from 'c/Button';
|
||||||
import { FixedSizeList as List, areEqual } from 'react-window';
|
import { FixedSizeList as List, areEqual } from 'react-window';
|
||||||
|
import { RotateCw } from 'react-feather';
|
||||||
|
|
||||||
import ContentHeader from 'c/ContentHeader';
|
import ContentHeader from 'c/ContentHeader';
|
||||||
import Rule from 'c/Rule';
|
import Rule from 'c/Rule';
|
||||||
|
@ -10,7 +11,9 @@ import useRemainingViewPortHeight from '../hooks/useRemainingViewPortHeight';
|
||||||
|
|
||||||
import { getRules, fetchRules, fetchRulesOnce } from 'd/rules';
|
import { getRules, fetchRules, fetchRulesOnce } from 'd/rules';
|
||||||
|
|
||||||
import s0 from './Rules.module.css';
|
const { memo, useEffect, useMemo } = React;
|
||||||
|
|
||||||
|
// import s from './Rules.module.css';
|
||||||
const paddingBottom = 30;
|
const paddingBottom = 30;
|
||||||
|
|
||||||
const mapStateToProps = s => ({
|
const mapStateToProps = s => ({
|
||||||
|
@ -43,7 +46,7 @@ export default function Rules() {
|
||||||
fetchRulesOnce();
|
fetchRulesOnce();
|
||||||
}, [fetchRulesOnce]);
|
}, [fetchRulesOnce]);
|
||||||
const [refRulesContainer, containerHeight] = useRemainingViewPortHeight();
|
const [refRulesContainer, containerHeight] = useRemainingViewPortHeight();
|
||||||
|
const refreshIcon = useMemo(() => <RotateCw width={16} />, []);
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<ContentHeader title="Rules" />
|
<ContentHeader title="Rules" />
|
||||||
|
@ -60,8 +63,12 @@ export default function Rules() {
|
||||||
{Row}
|
{Row}
|
||||||
</List>
|
</List>
|
||||||
</div>
|
</div>
|
||||||
<div className={s0.fabgrp}>
|
<div className="fabgrp">
|
||||||
<Button label="Refresh" onClick={fetchRules} />
|
<ButtonWithIcon
|
||||||
|
text="Refresh"
|
||||||
|
icon={refreshIcon}
|
||||||
|
onClick={fetchRules}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,5 +1 @@
|
||||||
.fabgrp {
|
/* */
|
||||||
position: fixed;
|
|
||||||
right: 20px;
|
|
||||||
bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
|
@ -12,8 +12,6 @@ function SvgYacd({
|
||||||
c1 = '#eee'
|
c1 = '#eee'
|
||||||
}) {
|
}) {
|
||||||
const faceClasName = cx({ [s.path]: animate });
|
const faceClasName = cx({ [s.path]: animate });
|
||||||
// fill="#2A477A"
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
width={width}
|
width={width}
|
||||||
|
@ -26,7 +24,7 @@ function SvgYacd({
|
||||||
<path
|
<path
|
||||||
d="M71.689 53.055c9.23-1.487 25.684 27.263 41.411 56.663 18.572-8.017 71.708-7.717 93.775 0 4.714-15.612 31.96-57.405 41.626-56.663 3.992.088 13.07 31.705 23.309 94.96 2.743 16.949 7.537 47.492 14.38 91.63-42.339 17.834-84.37 26.751-126.095 26.751-41.724 0-83.756-8.917-126.095-26.751C52.973 116.244 65.536 54.047 71.689 53.055z"
|
d="M71.689 53.055c9.23-1.487 25.684 27.263 41.411 56.663 18.572-8.017 71.708-7.717 93.775 0 4.714-15.612 31.96-57.405 41.626-56.663 3.992.088 13.07 31.705 23.309 94.96 2.743 16.949 7.537 47.492 14.38 91.63-42.339 17.834-84.37 26.751-126.095 26.751-41.724 0-83.756-8.917-126.095-26.751C52.973 116.244 65.536 54.047 71.689 53.055z"
|
||||||
stroke={c1}
|
stroke={c1}
|
||||||
strokeWidth="2"
|
strokeWidth="4"
|
||||||
strokeLinecap="round"
|
strokeLinecap="round"
|
||||||
fill={c0}
|
fill={c0}
|
||||||
className={faceClasName}
|
className={faceClasName}
|
||||||
|
|
23
yarn.lock
23
yarn.lock
|
@ -336,6 +336,13 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/helper-plugin-utils" "^7.0.0"
|
"@babel/helper-plugin-utils" "^7.0.0"
|
||||||
|
|
||||||
|
"@babel/plugin-syntax-flow@^7.7.4":
|
||||||
|
version "7.7.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.7.4.tgz#6d91b59e1a0e4c17f36af2e10dd64ef220919d7b"
|
||||||
|
integrity sha512-2AMAWl5PsmM5KPkB22cvOkUyWk6MjUaqhHNU5nSPUl/ns3j5qLfw2SuYP5RbVZ0tfLvePr4zUScbICtDP2CUNw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/helper-plugin-utils" "^7.0.0"
|
||||||
|
|
||||||
"@babel/plugin-syntax-json-strings@^7.2.0":
|
"@babel/plugin-syntax-json-strings@^7.2.0":
|
||||||
version "7.2.0"
|
version "7.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.2.0.tgz#72bd13f6ffe1d25938129d2a186b11fd62951470"
|
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.2.0.tgz#72bd13f6ffe1d25938129d2a186b11fd62951470"
|
||||||
|
@ -453,6 +460,14 @@
|
||||||
"@babel/helper-builder-binary-assignment-operator-visitor" "^7.1.0"
|
"@babel/helper-builder-binary-assignment-operator-visitor" "^7.1.0"
|
||||||
"@babel/helper-plugin-utils" "^7.0.0"
|
"@babel/helper-plugin-utils" "^7.0.0"
|
||||||
|
|
||||||
|
"@babel/plugin-transform-flow-strip-types@^7.7.4":
|
||||||
|
version "7.7.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.7.4.tgz#cc73f85944782df1d77d80977bc097920a8bf31a"
|
||||||
|
integrity sha512-w9dRNlHY5ElNimyMYy0oQowvQpwt/PRHI0QS98ZJCTZU2bvSnKXo5zEiD5u76FBPigTm8TkqzmnUTg16T7qbkA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/helper-plugin-utils" "^7.0.0"
|
||||||
|
"@babel/plugin-syntax-flow" "^7.7.4"
|
||||||
|
|
||||||
"@babel/plugin-transform-for-of@^7.4.4":
|
"@babel/plugin-transform-for-of@^7.4.4":
|
||||||
version "7.4.4"
|
version "7.4.4"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.4.4.tgz#0267fc735e24c808ba173866c6c4d1440fc3c556"
|
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.4.4.tgz#0267fc735e24c808ba173866c6c4d1440fc3c556"
|
||||||
|
@ -714,6 +729,14 @@
|
||||||
js-levenshtein "^1.1.3"
|
js-levenshtein "^1.1.3"
|
||||||
semver "^5.5.0"
|
semver "^5.5.0"
|
||||||
|
|
||||||
|
"@babel/preset-flow@^7.7.4":
|
||||||
|
version "7.7.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.7.4.tgz#99c1349b6fd7132783196de181e6b32d0949427e"
|
||||||
|
integrity sha512-6LbUqcHD8BcRtXMOp5bc5nJeU8RlKh6q5U8TgZeCrf9ebBdW8Wyy5ujAUnbJfmzQ56Kkq5XtwErC/5+5RHyFYA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/helper-plugin-utils" "^7.0.0"
|
||||||
|
"@babel/plugin-transform-flow-strip-types" "^7.7.4"
|
||||||
|
|
||||||
"@babel/preset-react@^7.7.0":
|
"@babel/preset-react@^7.7.0":
|
||||||
version "7.7.0"
|
version "7.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.7.0.tgz#8ab0c4787d98cf1f5f22dabf115552bf9e4e406c"
|
resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.7.0.tgz#8ab0c4787d98cf1f5f22dabf115552bf9e4e406c"
|
||||||
|
|
Loading…
Reference in a new issue