Chore: add more strictly eslint

This commit is contained in:
Dreamacro 2021-07-01 23:56:50 +08:00
parent 5ff1742361
commit df0bfb5e10
35 changed files with 351 additions and 253 deletions

View File

@ -1,3 +1,17 @@
extends:
- standard-with-typescript
- react-app
parser: '@typescript-eslint/parser'
parserOptions:
project: './tsconfig.json'
rules:
comma-dangle: [error, always-multiline]
'@typescript-eslint/indent': [error, 4]
'@typescript-eslint/explicit-function-return-type': off
'@typescript-eslint/restrict-template-expressions': off
'@typescript-eslint/strict-boolean-expressions': off
'@typescript-eslint/no-non-null-assertion': off
'@typescript-eslint/consistent-type-assertions': off
'@typescript-eslint/promise-function-async': off
'@typescript-eslint/no-floating-promises': off
'@typescript-eslint/no-invalid-void-type': off

View File

@ -39,9 +39,12 @@
"babel-eslint": "^10.1.0",
"eslint": "^7.29.0",
"eslint-config-react-app": "^6.0.0",
"eslint-config-standard-with-typescript": "^20.0.0",
"eslint-plugin-flowtype": "^5.7.2",
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^5.1.0",
"eslint-plugin-react": "^7.24.0",
"eslint-plugin-react-hooks": "^4.2.0",
"sass": "^1.35.1",

View File

@ -14,7 +14,7 @@ const iconMap = {
success: 'check',
info: 'info',
warning: 'info',
error: 'close'
error: 'close',
}
export function Alert (props: AlertProps) {

View File

@ -10,7 +10,7 @@ export interface ButtonSelectOptions<T = string> {
export interface ButtonSelectProps<T = string> extends BaseComponentProps {
// options
options: ButtonSelectOptions<T>[]
options: Array<ButtonSelectOptions<T>>
// active value
value: T

View File

@ -26,7 +26,7 @@ export function Input (props: InputProps) {
type = 'text',
disabled = false,
onChange = noop,
onBlur = noop
onBlur = noop,
} = props
const classname = classnames('input', `text-${align}`, { 'focus:shadow-none': inside }, className)

View File

@ -10,7 +10,7 @@ const TYPE_ICON_MAP = {
info: 'info',
success: 'check',
warning: 'info-o',
error: 'close'
error: 'close',
}
type NoticeType = 'success' | 'info' | 'warning' | 'error'
@ -38,7 +38,7 @@ export function Message (props: MessageProps) {
icon = <Icon type="info" size={16} />,
content = '',
type = 'info',
duration = 1500
duration = 1500,
} = props
const { visible, show, hide } = useVisible()
@ -85,7 +85,7 @@ export function showMessage (args: ArgsProps) {
content,
removeComponent,
duration,
onClose
onClose,
}
render(<Message {...props} />, container)
@ -94,23 +94,23 @@ export function showMessage (args: ArgsProps) {
export const info = (
content: string,
duration?: number,
onClose?: typeof noop
onClose?: typeof noop,
) => showMessage({ type: 'info', content, duration, onClose })
export const success = (
content: string,
duration?: number,
onClose?: typeof noop
onClose?: typeof noop,
) => showMessage({ type: 'success', content, duration, onClose })
export const warning = (
content: string,
duration?: number,
onClose?: typeof noop
onClose?: typeof noop,
) => showMessage({ type: 'warning', content, duration, onClose })
export const error = (
content: string,
duration?: number,
onClose?: typeof noop
onClose?: typeof noop,
) => showMessage({ type: 'error', content, duration, onClose })

View File

@ -45,7 +45,7 @@ export function Modal (props: ModalProps) {
bodyStyle,
className,
style,
children
children,
} = props
const { translation } = useI18n()

View File

@ -23,18 +23,18 @@ interface SelectProps extends BaseComponentProps {
export function Select (props: SelectProps) {
const { value, onSelect, children, className: cn, style } = props
const portalRef = useRef<HTMLDivElement>()
const portalRef = useRef<HTMLDivElement>(document.createElement('div'))
const attachmentRef = useRef<HTMLDivElement>(null)
const targetRef = useRef<HTMLDivElement>(null)
const [showDropDownList, setShowDropDownList] = useState(false)
const [hasCreateDropList, setHasCreateDropList] = useState(false)
const dropdownListStyles = useMemo(() => {
if (targetRef.current) {
if (targetRef.current != null) {
const targetRectInfo = targetRef.current.getBoundingClientRect()
return {
top: Math.floor(targetRectInfo.top) - 10,
left: Math.floor(targetRectInfo.left) - 10
left: Math.floor(targetRectInfo.left) - 10,
}
}
return {}
@ -49,23 +49,17 @@ export function Select (props: SelectProps) {
}
useLayoutEffect(() => {
const current = portalRef.current
document.body.appendChild(current)
document.addEventListener('click', handleGlobalClick, true)
return () => {
document.addEventListener('click', handleGlobalClick, true)
if (portalRef.current) {
document.body.removeChild(portalRef.current)
}
document.body.removeChild(current)
}
}, [])
function handleShowDropList () {
if (!hasCreateDropList) {
if (!portalRef.current) {
// create container element
const container = document.createElement('div')
document.body.appendChild(container)
portalRef.current = container
}
setHasCreateDropList(true)
}
setShowDropDownList(true)
@ -100,9 +94,9 @@ export function Select (props: SelectProps) {
onClick: (e: React.MouseEvent<HTMLLIElement>) => {
onSelect?.(child.props.value, e)
setShowDropDownList(false)
rawOnClickEvent && rawOnClickEvent(e)
rawOnClickEvent?.(e)
},
className
className,
}))
})
}, [children, value, onSelect])
@ -131,7 +125,7 @@ export function Select (props: SelectProps) {
<Icon type="triangle-down" />
</div>
{
hasCreateDropList && createPortal(dropDownList, portalRef?.current!)
hasCreateDropList && createPortal(dropDownList, portalRef.current)
}
</>
)

View File

@ -25,7 +25,7 @@ export default function App () {
{ path: '/logs', name: 'Logs', component: Logs },
{ path: '/rules', name: 'Rules', component: Rules, noMobile: true },
{ path: '/connections', name: 'Connections', component: Connections, noMobile: true },
{ path: '/settings', name: 'Settings', component: Settings }
{ path: '/settings', name: 'Settings', component: Settings },
]
return (
@ -36,7 +36,7 @@ export default function App () {
<Route exact path="/" component={() => <Redirect to="/proxies"/>} />
{
routes.map(
route => <Route exact={false} path={route.path} key={route.path} component={route.component} />
route => <Route exact={false} path={route.path} key={route.path} component={route.component} />,
)
}
</Switch>

View File

@ -34,7 +34,7 @@ export function Devices (props: DevicesProps) {
onClick={() => handleSelected(device.label)}>
{ device.label } ({ device.number })
</div>
)
),
)
}
</div>

View File

@ -47,7 +47,7 @@ interface ITableInstance<D extends object = {}> extends
TableInstance<D>,
UseFiltersInstanceProps<D> {}
function formatTraffic(num: number) {
function formatTraffic (num: number) {
const s = ['B', 'KB', 'MB', 'GB', 'TB']
let idx = 0
while (~~(num / 1024) && idx < s.length) {
@ -58,7 +58,7 @@ function formatTraffic(num: number) {
return `${idx === 0 ? num : num.toFixed(2)} ${s[idx]}`
}
function formatSpeed(upload: number, download: number) {
function formatSpeed (upload: number, download: number) {
switch (true) {
case upload === 0 && download === 0:
return '-'
@ -89,7 +89,7 @@ interface formatConnection {
completed: boolean
}
export default function Connections() {
export default function Connections () {
const { translation, lang } = useI18n()
const t = useMemo(() => translation('Connections').t, [translation])
const connStreamReader = useConnectionStreamReader()
@ -98,12 +98,12 @@ export default function Connections() {
// total
const [traffic, setTraffic] = useObject({
uploadTotal: 0,
downloadTotal: 0
downloadTotal: 0,
})
// close all connections
const { visible, show, hide } = useVisible()
function handleCloseConnections() {
function handleCloseConnections () {
client.closeAllConnections().finally(() => hide())
}
@ -122,8 +122,8 @@ export default function Connections() {
type: c.metadata.type,
network: c.metadata.network.toUpperCase(),
speed: { upload: c.uploadSpeed, download: c.downloadSpeed },
completed: !!c.completed
})
completed: !!c.completed,
}),
), [connections])
const devices = useMemo(() => {
const gb = groupBy(connections, 'metadata.sourceIP')
@ -133,7 +133,7 @@ export default function Connections() {
// table
const tableRef = useRef<HTMLDivElement>(null)
const { x: scrollX } = useScroll(tableRef)
const columns: TableColumnOption<formatConnection>[] = useMemo(() => [
const columns: Array<TableColumnOption<formatConnection>> = useMemo(() => [
{ Header: t(`columns.${Columns.Host}`), accessor: Columns.Host, minWidth: 260, width: 260 },
{ Header: t(`columns.${Columns.Network}`), accessor: Columns.Network, minWidth: 80, width: 80 },
{ Header: t(`columns.${Columns.Type}`), accessor: Columns.Type, minWidth: 120, width: 120 },
@ -142,31 +142,32 @@ export default function Connections() {
{
id: Columns.Speed,
Header: t(`columns.${Columns.Speed}`),
accessor(originalRow: formatConnection) {
accessor (originalRow: formatConnection) {
return [originalRow.speed.upload, originalRow.speed.download]
},
sortType(rowA, rowB) {
sortType (rowA, rowB) {
const speedA = rowA.original.speed
const speedB = rowB.original.speed
return speedA.download === speedB.download
? speedA.upload - speedB.upload
: speedA.download - speedB.download
},
minWidth: 200, width: 200,
sortDescFirst: true
minWidth: 200,
width: 200,
sortDescFirst: true,
},
{ Header: t(`columns.${Columns.Upload}`), accessor: Columns.Upload, minWidth: 100, width: 100, sortDescFirst: true },
{ Header: t(`columns.${Columns.Download}`), accessor: Columns.Download, minWidth: 100, width: 100, sortDescFirst: true },
{ Header: t(`columns.${Columns.SourceIP}`), accessor: Columns.SourceIP, minWidth: 140, width: 140 },
{ Header: t(`columns.${Columns.Time}`), accessor: Columns.Time, minWidth: 120, width: 120, sortType(rowA, rowB) { return rowB.original.time - rowA.original.time } },
] as TableColumnOption<formatConnection>[], [t])
{ Header: t(`columns.${Columns.Time}`), accessor: Columns.Time, minWidth: 120, width: 120, sortType (rowA, rowB) { return rowB.original.time - rowA.original.time } },
] as Array<TableColumnOption<formatConnection>>, [t])
useLayoutEffect(() => {
function handleConnection(snapshots: API.Snapshot[]) {
function handleConnection (snapshots: API.Snapshot[]) {
for (const snapshot of snapshots) {
setTraffic({
uploadTotal: snapshot.uploadTotal,
downloadTotal: snapshot.downloadTotal
downloadTotal: snapshot.downloadTotal,
})
feed(snapshot.connections)
@ -186,19 +187,19 @@ export default function Connections() {
headerGroups,
rows,
prepareRow,
setFilter
setFilter,
} = useTable(
{
columns,
data,
autoResetSortBy: false,
autoResetFilters: false,
initialState: { sortBy: [{ id: Columns.Time, desc: false }] }
initialState: { sortBy: [{ id: Columns.Time, desc: false }] },
} as ITableOptions<formatConnection>,
useResizeColumns,
useBlockLayout,
useFilters,
useSortBy
useSortBy,
) as ITableInstance<formatConnection>
const headerGroup = useMemo(() => headerGroups[0], [headerGroups])
const renderCell = useCallback(function (cell: Cell<formatConnection>) {
@ -244,7 +245,7 @@ export default function Connections() {
{...realColumn.getHeaderProps()}
className={classnames('connections-th', {
resizing: realColumn.isResizing,
fixed: scrollX > 0 && realColumn.id === Columns.Host
fixed: scrollX > 0 && realColumn.id === Columns.Host,
})}
key={id}>
<div {...realColumn.getSortByToggleProps()}>
@ -275,7 +276,7 @@ export default function Connections() {
const classname = classnames(
'connections-block',
{ 'text-center': shouldCenter.has(cell.column.id), completed: row.original.completed },
{ fixed: scrollX > 0 && cell.column.id === Columns.Host }
{ fixed: scrollX > 0 && cell.column.id === Columns.Host },
)
return (
<div {...cell.getCellProps()} className={classname} key={cell.column.id}>

View File

@ -9,7 +9,7 @@ class Store {
appendToSet (connections: API.Connections[]) {
const mapping = connections.reduce(
(map, c) => map.set(c.id, c), new Map<string, API.Connections>()
(map, c) => map.set(c.id, c), new Map<string, API.Connections>(),
)
for (const id of this.connections.keys()) {
@ -18,7 +18,7 @@ class Store {
this.connections.delete(id)
} else {
const connection = this.connections.get(id)
if (connection) {
if (connection != null) {
connection.completed = true
connection.uploadSpeed = 0
connection.downloadSpeed = 0

View File

@ -15,7 +15,7 @@ export default function ExternalController () {
const [value, set] = useObject({
hostname: '',
port: '',
secret: ''
secret: '',
})
useEffect(() => {

View File

@ -15,7 +15,7 @@ export default function Logs () {
useLayoutEffect(() => {
const ul = listRef.current
if (ul) {
if (ul != null) {
ul.scrollTop = ul.scrollHeight
}
})
@ -26,7 +26,7 @@ export default function Logs () {
setLogs(logsRef.current)
}
if (logsStreamReader) {
if (logsStreamReader != null) {
logsStreamReader.subscribe('data', handleLog)
logsRef.current = logsStreamReader.buffer()
setLogs(logsRef.current)
@ -47,7 +47,7 @@ export default function Logs () {
<span className="mr-4 text-gray-400 text-opacity-90">{ dayjs(log.time).format('YYYY-MM-DD HH:mm:ss') }</span>
<span>[{ log.type }] { log.payload }</span>
</li>
)
),
)
}
</ul>

View File

@ -9,10 +9,10 @@ export default function Overview () {
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column',
opacity: 0.3
opacity: 0.3,
}}>
<img src={logo} alt="Logo" style={{
width: 200
width: 200,
}}/>
<h1 style={{ color: '#54759A', marginTop: 20 }}>Coming Soon...</h1>

View File

@ -28,9 +28,7 @@ export function Group (props: GroupProps) {
}
}
for (const id of list) {
client.closeConnection(id)
}
await Promise.all(list.map(id => client.closeConnection(id)))
}
}

View File

@ -24,12 +24,12 @@ export function Provider (props: ProvidersProps) {
function handleHealthChech () {
show()
client.healthCheckProvider(provider.name).then(() => update()).finally(() => hide())
client.healthCheckProvider(provider.name).then(async () => await update()).finally(() => hide())
}
function handleUpdate () {
show()
client.updateProvider(provider.name).then(() => update()).finally(() => hide())
client.updateProvider(provider.name).then(async () => await update()).finally(() => hide())
}
const proxies = useMemo(() => {

View File

@ -18,7 +18,7 @@ const TagColors = {
'#909399': 0,
'#00c520': 260,
'#ff9a28': 600,
'#ff3e5e': Infinity
'#ff3e5e': Infinity,
}
export function Proxy (props: ProxyProps) {
@ -42,7 +42,7 @@ export function Proxy (props: ProxyProps) {
const validDelay = result.isErr() ? 0 : result.value
set(draft => {
const proxy = draft.proxies.find(p => p.name === config.name)
if (proxy) {
if (proxy != null) {
proxy.history.push({ time: Date.now().toString(), delay: validDelay })
}
})
@ -50,20 +50,21 @@ export function Proxy (props: ProxyProps) {
const delay = useMemo(
() => config.history?.length ? config.history.slice(-1)[0].delay : 0,
[config]
[config],
)
useLayoutEffect(() => {
EE.subscribe(Action.SPEED_NOTIFY, speedTest)
return () => EE.unsubscribe(Action.SPEED_NOTIFY, speedTest)
const handler = () => { speedTest() }
EE.subscribe(Action.SPEED_NOTIFY, handler)
return () => EE.unsubscribe(Action.SPEED_NOTIFY, handler)
}, [speedTest])
const hasError = useMemo(() => delay === 0, [delay])
const color = useMemo(() =>
Object.keys(TagColors).find(
threshold => delay <= TagColors[threshold as keyof typeof TagColors]
const color = useMemo(
() => Object.keys(TagColors).find(
threshold => delay <= TagColors[threshold as keyof typeof TagColors],
),
[delay]
[delay],
)
const backgroundColor = hasError ? undefined : color

View File

@ -17,12 +17,12 @@ enum sortType {
const sortMap = {
[sortType.None]: 'sort',
[sortType.Asc]: 'sort-ascending',
[sortType.Desc]: 'sort-descending'
[sortType.Desc]: 'sort-descending',
}
export function compareDesc (a: API.Proxy, b: API.Proxy) {
const lastDelayA = a.history.length ? a.history.slice(-1)[0].delay : 0
const lastDelayB = b.history.length ? b.history.slice(-1)[0].delay : 0
const lastDelayA = (a.history.length > 0) ? a.history.slice(-1)[0].delay : 0
const lastDelayB = (b.history.length > 0) ? b.history.slice(-1)[0].delay : 0
return (lastDelayB || Number.MAX_SAFE_INTEGER) - (lastDelayA || Number.MAX_SAFE_INTEGER)
}
@ -35,7 +35,7 @@ function ProxyGroups () {
const list = useMemo(
() => general.mode === 'global' ? [global] : groups,
[general, groups, global]
[general, groups, global],
)
return <>
@ -100,7 +100,7 @@ function Proxies () {
}
const { current: sort, next } = useRound(
[sortType.Asc, sortType.Desc, sortType.None]
[sortType.Asc, sortType.Desc, sortType.None],
)
const sortedProxies = useMemo(() => {
switch (sort) {

View File

@ -23,7 +23,7 @@ export function Provider (props: ProvidersProps) {
function handleUpdate () {
show()
client.updateRuleProvider(provider.name).then(() => update()).finally(() => hide())
client.updateRuleProvider(provider.name).then(async () => await update()).finally(() => hide())
}
const updateClassnames = classnames('rule-provider-icon', { 'rule-provider-loading': visible })

View File

@ -23,7 +23,7 @@ export default function Settings () {
const [info, set] = useObject({
socks5ProxyPort: 7891,
httpProxyPort: 7890,
mixedProxyPort: 0
mixedProxyPort: 0,
})
useEffect(() => {
@ -39,12 +39,12 @@ export default function Settings () {
async function handleStartAtLoginChange (state: boolean) {
await jsBridge?.setStartAtLogin(state)
fetchClashXData()
await fetchClashXData()
}
async function handleSetSystemProxy (state: boolean) {
await jsBridge?.setSystemProxy(state)
fetchClashXData()
await fetchClashXData()
}
function changeLanguage (language: Lang) {
@ -73,7 +73,7 @@ export default function Settings () {
const {
hostname: externalControllerHost,
port: externalControllerPort
port: externalControllerPort,
} = apiInfo
const { allowLan, mode } = general
@ -86,7 +86,7 @@ export default function Settings () {
const options = [
{ label: t('values.global'), value: 'Global' },
{ label: t('values.rules'), value: 'Rule' },
{ label: t('values.direct'), value: 'Direct' }
{ label: t('values.direct'), value: 'Direct' },
]
if (premium) {
options.push({ label: t('values.script'), value: 'Script' })

View File

@ -7,12 +7,12 @@ import logo from '@assets/logo.png'
import './style.scss'
interface SidebarProps {
routes: {
routes: Array<{
path: string
name: string
noMobile?: boolean
exact?: boolean
}[]
}>
}
export default function Sidebar (props: SidebarProps) {
@ -27,7 +27,7 @@ export default function Sidebar (props: SidebarProps) {
<li className={classnames('item', { 'no-mobile': noMobile })} key={name}>
<NavLink to={path} activeClassName="active" exact={!!exact}>{ t(name) }</NavLink>
</li>
)
),
)
return (

View File

@ -6,7 +6,7 @@ const EN = {
Rules: 'Rules',
Settings: 'Setting',
Connections: 'Connections',
Version: 'Version'
Version: 'Version',
},
Settings: {
title: 'Settings',
@ -19,7 +19,7 @@ const EN = {
socks5ProxyPort: 'Socks5 proxy port',
httpProxyPort: 'HTTP proxy port',
mixedProxyPort: 'Mixed proxy port',
externalController: 'External controller'
externalController: 'External controller',
},
values: {
cn: '中文',
@ -27,7 +27,7 @@ const EN = {
global: 'Global',
rules: 'Rules',
direct: 'Direct',
script: 'Script'
script: 'Script',
},
versionString: 'Current ClashX is the latest version{{version}}',
checkUpdate: 'Check Update',
@ -36,17 +36,17 @@ const EN = {
note: 'Please note that modifying this configuration will only configure Dashboard. Will not modify your Clash configuration file. Please make sure that the external controller address matches the address in the Clash configuration file, otherwise, Dashboard will not be able to connect to Clash.',
host: 'Host',
port: 'Port',
secret: 'Secret'
}
secret: 'Secret',
},
},
Logs: {
title: 'Logs'
title: 'Logs',
},
Rules: {
title: 'Rules',
providerTitle: 'Providers',
providerUpdateTime: 'Last updated at',
ruleCount: 'Rule count'
ruleCount: 'Rule count',
},
Connections: {
title: 'Connections',
@ -54,14 +54,14 @@ const EN = {
total: {
text: 'total',
upload: 'upload',
download: 'download'
download: 'download',
},
closeAll: {
title: 'Warning',
content: 'This would close all connections'
content: 'This would close all connections',
},
filter: {
all: 'All'
all: 'All',
},
columns: {
host: 'Host',
@ -73,8 +73,8 @@ const EN = {
speed: 'Speed',
upload: 'Upload',
download: 'Download',
sourceIP: 'Source IP'
}
sourceIP: 'Source IP',
},
},
Proxies: {
title: 'Proxies',
@ -91,7 +91,7 @@ const EN = {
'obfs-host': 'Obfs-host',
uuid: 'UUID',
alterId: 'AlterId',
tls: 'TLS'
tls: 'TLS',
},
groupTitle: 'Policy Group',
providerTitle: 'Providers',
@ -99,12 +99,12 @@ const EN = {
expandText: 'Expand',
collapseText: 'Collapse',
speedTestText: 'Speed Test',
breakConnectionsText: 'Close connections which include the group'
breakConnectionsText: 'Close connections which include the group',
},
Modal: {
ok: 'Ok',
cancel: 'Cancel'
}
cancel: 'Cancel',
},
}
export default EN

View File

@ -3,7 +3,7 @@ import zh_CN from './zh_CN'
export const Language = {
en_US,
zh_CN
zh_CN,
}
export type Lang = keyof typeof Language

View File

@ -6,7 +6,7 @@ const CN = {
Rules: '规则',
Settings: '设置',
Connections: '连接',
Version: '版本'
Version: '版本',
},
Settings: {
title: '设置',
@ -19,7 +19,7 @@ const CN = {
socks5ProxyPort: 'Socks5 代理端口',
httpProxyPort: 'HTTP 代理端口',
mixedProxyPort: '混合代理端口',
externalController: '外部控制设置'
externalController: '外部控制设置',
},
values: {
cn: '中文',
@ -27,7 +27,7 @@ const CN = {
global: '全局',
rules: '规则',
direct: '直连',
script: '脚本'
script: '脚本',
},
versionString: '当前 ClashX 已是最新版本:{{version}}',
checkUpdate: '检查更新',
@ -36,17 +36,17 @@ const CN = {
note: '请注意,修改该配置项并不会修改你的 Clash 配置文件,请确认修改后的外部控制地址和 Clash 配置文件内的地址一致,否则会导致 Dashboard 无法连接。',
host: 'Host',
port: '端口',
secret: '密钥'
}
secret: '密钥',
},
},
Logs: {
title: '日志'
title: '日志',
},
Rules: {
title: '规则',
providerTitle: '规则集',
providerUpdateTime: '最后更新于',
ruleCount: '规则条数'
ruleCount: '规则条数',
},
Connections: {
title: '连接',
@ -54,14 +54,14 @@ const CN = {
total: {
text: '总量',
upload: '上传',
download: '下载'
download: '下载',
},
closeAll: {
title: '警告',
content: '将会关闭所有连接'
content: '将会关闭所有连接',
},
filter: {
all: '全部'
all: '全部',
},
columns: {
host: '域名',
@ -73,8 +73,8 @@ const CN = {
speed: '速率',
upload: '上传',
download: '下载',
sourceIP: '来源 IP'
}
sourceIP: '来源 IP',
},
},
Proxies: {
title: '代理',
@ -91,7 +91,7 @@ const CN = {
'obfs-host': 'Obfs-host',
uuid: 'UUID',
alterId: 'AlterId',
tls: 'TLS'
tls: 'TLS',
},
groupTitle: '策略组',
providerTitle: '代理集',
@ -99,12 +99,12 @@ const CN = {
expandText: '展开',
collapseText: '收起',
speedTestText: '测速',
breakConnectionsText: '切换时打断包含策略组的连接'
breakConnectionsText: '切换时打断包含策略组的连接',
},
Modal: {
ok: '确 定',
cancel: '取 消'
}
cancel: '取 消',
},
}
export default CN

View File

@ -2,11 +2,11 @@ export function createAsyncSingleton<T> (fn: () => Promise<T>): () => Promise<T>
let promise: Promise<T> | null = null
return async function () {
if (promise) {
return promise
if (promise != null) {
return await promise
}
promise = fn()
return promise
return await promise
.catch(e => {
promise = null
throw e

View File

@ -53,7 +53,7 @@ export function useInterval (callback: () => void, delay: number) {
return () => clearInterval(id)
}
},
[delay]
[delay],
)
}

View File

@ -73,7 +73,7 @@ export class JsBridge {
instance: JsBridgeAPI | null = null
constructor (callback: () => void) {
if (window.WebViewJavascriptBridge) {
if (window.WebViewJavascriptBridge != null) {
this.instance = window.WebViewJavascriptBridge
}
@ -97,12 +97,12 @@ export class JsBridge {
return callback?.(null)
}
if (window.WebViewJavascriptBridge) {
if (window.WebViewJavascriptBridge != null) {
return callback(window.WebViewJavascriptBridge)
}
// setup callback
if (window.WVJBCallbacks) {
if (window.WVJBCallbacks != null) {
return window.WVJBCallbacks.push(callback)
}
@ -115,63 +115,63 @@ export class JsBridge {
setTimeout(() => document.documentElement.removeChild(WVJBIframe), 0)
}
public callHandler<T> (handleName: string, data?: any) {
return new Promise<T>((resolve) => {
public async callHandler<T> (handleName: string, data?: any) {
return await new Promise<T>((resolve) => {
this.instance?.callHandler(
handleName,
data,
resolve
resolve,
)
})
}
public ping () {
return this.callHandler('ping')
public async ping () {
return await this.callHandler('ping')
}
public readConfigString () {
return this.callHandler<string>('readConfigString')
public async readConfigString () {
return await this.callHandler<string>('readConfigString')
}
public getPasteboard () {
return this.callHandler<string>('getPasteboard')
public async getPasteboard () {
return await this.callHandler<string>('getPasteboard')
}
public getAPIInfo () {
return this.callHandler<{ host: string, port: string, secret: string }>('apiInfo')
public async getAPIInfo () {
return await this.callHandler<{ host: string, port: string, secret: string }>('apiInfo')
}
public setPasteboard (data: string) {
return this.callHandler('setPasteboard', data)
public async setPasteboard (data: string) {
return await this.callHandler('setPasteboard', data)
}
public writeConfigWithString (data: string) {
return this.callHandler('writeConfigWithString', data)
public async writeConfigWithString (data: string) {
return await this.callHandler('writeConfigWithString', data)
}
public setSystemProxy (data: boolean) {
return this.callHandler('setSystemProxy', data)
public async setSystemProxy (data: boolean) {
return await this.callHandler('setSystemProxy', data)
}
public getStartAtLogin () {
return this.callHandler<boolean>('getStartAtLogin')
public async getStartAtLogin () {
return await this.callHandler<boolean>('getStartAtLogin')
}
public getProxyDelay (name: string) {
return this.callHandler<number>('speedTest', name)
public async getProxyDelay (name: string) {
return await this.callHandler<number>('speedTest', name)
}
public setStartAtLogin (data: boolean) {
return this.callHandler<boolean>('setStartAtLogin', data)
public async setStartAtLogin (data: boolean) {
return await this.callHandler<boolean>('setStartAtLogin', data)
}
public isSystemProxySet () {
return this.callHandler<boolean>('isSystemProxySet')
public async isSystemProxySet () {
return await this.callHandler<boolean>('isSystemProxySet')
}
}
export function setupJsBridge (callback: () => void) {
if (jsBridge) {
if (jsBridge != null) {
callback()
return
}

View File

@ -94,32 +94,32 @@ export interface Connections {
}
export class Client {
private axiosClient: AxiosInstance
constructor(url: string, secret?: string) {
private readonly axiosClient: AxiosInstance
constructor (url: string, secret?: string) {
this.axiosClient = axios.create({
baseURL: url,
headers: secret ? { Authorization: `Bearer ${secret}` } : {}
headers: secret ? { Authorization: `Bearer ${secret}` } : {},
})
}
getConfig() {
return this.axiosClient.get<Config>('configs')
async getConfig () {
return await this.axiosClient.get<Config>('configs')
}
updateConfig(config: Partial<Config>) {
return this.axiosClient.patch<void>('configs', config)
async updateConfig (config: Partial<Config>) {
return await this.axiosClient.patch<void>('configs', config)
}
getRules() {
return this.axiosClient.get<Rules>('rules')
async getRules () {
return await this.axiosClient.get<Rules>('rules')
}
async getProxyProviders () {
const resp = await this.axiosClient.get<ProxyProviders>('providers/proxies', {
validateStatus(status) {
validateStatus (status) {
// compatible old version
return (status >= 200 && status < 300) || status === 404
}
},
})
if (resp.status === 404) {
resp.data = { providers: {} }
@ -127,56 +127,56 @@ export class Client {
return resp
}
getRuleProviders () {
return this.axiosClient.get<RuleProviders>('providers/rules')
async getRuleProviders () {
return await this.axiosClient.get<RuleProviders>('providers/rules')
}
updateProvider (name: string) {
return this.axiosClient.put<void>(`providers/proxies/${encodeURIComponent(name)}`)
async updateProvider (name: string) {
return await this.axiosClient.put<void>(`providers/proxies/${encodeURIComponent(name)}`)
}
updateRuleProvider (name: string) {
return this.axiosClient.put<void>(`providers/rules/${encodeURIComponent(name)}`)
async updateRuleProvider (name: string) {
return await this.axiosClient.put<void>(`providers/rules/${encodeURIComponent(name)}`)
}
healthCheckProvider (name: string) {
return this.axiosClient.get<void>(`providers/proxies/${encodeURIComponent(name)}/healthcheck`)
async healthCheckProvider (name: string) {
return await this.axiosClient.get<void>(`providers/proxies/${encodeURIComponent(name)}/healthcheck`)
}
getProxies () {
return this.axiosClient.get<Proxies>('proxies')
async getProxies () {
return await this.axiosClient.get<Proxies>('proxies')
}
getProxy (name: string) {
return this.axiosClient.get<Proxy>(`proxies/${encodeURIComponent(name)}`)
async getProxy (name: string) {
return await this.axiosClient.get<Proxy>(`proxies/${encodeURIComponent(name)}`)
}
getVersion () {
return this.axiosClient.get<{ version: string, premium?: boolean }>('version')
async getVersion () {
return await this.axiosClient.get<{ version: string, premium?: boolean }>('version')
}
getProxyDelay (name: string) {
return this.axiosClient.get<{ delay: number }>(`proxies/${encodeURIComponent(name)}/delay`, {
async getProxyDelay (name: string) {
return await this.axiosClient.get<{ delay: number }>(`proxies/${encodeURIComponent(name)}/delay`, {
params: {
timeout: 5000,
url: 'http://www.gstatic.com/generate_204'
}
url: 'http://www.gstatic.com/generate_204',
},
})
}
closeAllConnections () {
return this.axiosClient.delete('connections')
async closeAllConnections () {
return await this.axiosClient.delete('connections')
}
closeConnection (id: string) {
return this.axiosClient.delete(`connections/${id}`)
async closeConnection (id: string) {
return await this.axiosClient.delete(`connections/${id}`)
}
getConnections () {
return this.axiosClient.get<Snapshot>('connections')
async getConnections () {
return await this.axiosClient.get<Snapshot>('connections')
}
changeProxySelected (name: string, select: string) {
return this.axiosClient.put<void>(`proxies/${encodeURIComponent(name)}`, { name: select })
async changeProxySelected (name: string, select: string) {
return await this.axiosClient.put<void>(`proxies/${encodeURIComponent(name)}`, { name: select })
}
}

View File

@ -21,9 +21,9 @@ export class StreamReader<T> {
{
bufferLength: 0,
retryInterval: 5000,
headers: {}
headers: {},
},
config
config,
)
this.config.useWebsocket
@ -60,13 +60,13 @@ export class StreamReader<T> {
this.config.url,
{
mode: 'cors',
headers: this.config.token ? { Authorization: `Bearer ${this.config.token}` } : {}
}
headers: this.config.token ? { Authorization: `Bearer ${this.config.token}` } : {},
},
), e => e as Error)
if (result.isErr()) {
this.retry(result.error)
return
} else if (!result.value.body) {
} else if (result.value.body == null) {
this.retry(new Error('fetch body error'))
return
}
@ -87,7 +87,7 @@ export class StreamReader<T> {
const lines = decoder.decode(result.value.value).trim().split('\n')
const data = lines.map(l => JSON.parse(l))
this.EE.emit('data', data)
if (this.config.bufferLength! > 0) {
if (this.config.bufferLength > 0) {
this.innerBuffer.push(...data)
if (this.innerBuffer.length > this.config.bufferLength) {
this.innerBuffer.splice(0, this.innerBuffer.length - this.config.bufferLength)
@ -99,7 +99,7 @@ export class StreamReader<T> {
protected retry (err: Error) {
if (!this.isClose) {
this.EE.emit('error', err)
window.setTimeout(this.loop, this.config.retryInterval)
window.setTimeout(() => { this.loop() }, this.config.retryInterval)
}
}

View File

@ -20,7 +20,7 @@ export const SsCipher = [
'AES-256-CFB',
'CHACHA20',
'CHACHA20-IETF',
'XCHACHA20'
'XCHACHA20',
]
/**
@ -31,7 +31,7 @@ export const VmessCipher = [
'auto',
'none',
'aes-128-gcm',
'chacha20-poly1305'
'chacha20-poly1305',
]
/**
@ -53,5 +53,5 @@ export function pickCipherWithAlias (c: string) {
return 'AEAD_AES_256_GCM'
}
return SsCipher.find(c => c === cipher) || ''
return SsCipher.find(c => c === cipher) ?? ''
}

View File

@ -5,13 +5,13 @@
export const ProxyType = {
Shadowsocks: 'ss',
Vmess: 'vmess',
Socks5: 'socks5'
Socks5: 'socks5',
}
export type Proxy = ShadowsocksProxy & VmessProxy & Socks5Proxy
export const SsProxyConfigList = [
'name', 'type', 'server', 'port', 'cipher', 'password', 'obfs', 'obfs-host'
'name', 'type', 'server', 'port', 'cipher', 'password', 'obfs', 'obfs-host',
]
export interface ShadowsocksProxy {
name?: string
@ -32,7 +32,7 @@ export interface ShadowsocksProxy {
}
export const VmessProxyConfigList = [
'name', 'type', 'server', 'port', 'uuid', 'alterid', 'cipher', 'tls'
'name', 'type', 'server', 'port', 'uuid', 'alterid', 'cipher', 'tls',
]
export interface VmessProxy {
name?: string

View File

@ -34,7 +34,7 @@ export function useI18n () {
}
return { t }
},
[lang]
[lang],
)
return { lang, locales, setLang, translation }
@ -42,7 +42,7 @@ export function useI18n () {
export const version = atom({
version: '',
premium: false
premium: false,
})
export function useVersion () {
@ -57,7 +57,7 @@ export function useVersion () {
set(
result.isErr()
? { version: '', premium: false }
: { version: result.value.data.version, premium: !!result.value.data.premium }
: { version: result.value.data.version, premium: !!result.value.data.premium },
)
})
@ -83,7 +83,7 @@ export function useRuleProviders () {
}
export const configAtom = atomWithStorage('profile', {
breakConnections: false
breakConnections: false,
})
export function useConfig () {
@ -128,7 +128,7 @@ export function useGeneral () {
redirPort: data['redir-port'],
mode: data.mode.toLowerCase() as Models.Data['general']['mode'],
logLevel: data['log-level'],
allowLan: data['allow-lan']
allowLan: data['allow-lan'],
} as Models.Data['general']
})
@ -143,8 +143,8 @@ export const proxies = atomWithImmer({
type: 'Selector',
now: '',
history: [],
all: []
} as API.Group
all: [],
} as API.Group,
})
export function useProxy () {
@ -187,7 +187,7 @@ export function useProxy () {
global: allProxy.global,
update: mutate,
markProxySelected,
set
set,
}
}
@ -196,7 +196,7 @@ export const proxyMapping = atom((get) => {
const providers = get(proxyProvider)
const proxyMap = new Map<string, API.Proxy>()
for (const p of ps.proxies) {
proxyMap.set(p.name, p as API.Proxy)
proxyMap.set(p.name, p)
}
for (const provider of providers) {
@ -214,7 +214,7 @@ export function useClashXData () {
return {
isClashX: false,
startAtLogin: false,
systemProxy: false
systemProxy: false,
}
}
@ -244,7 +244,7 @@ export function useRule () {
const logsAtom = atom({
key: '',
instance: null as StreamReader<Log> | null
instance: null as StreamReader<Log> | null,
})
export function useLogsStreamReader () {
@ -269,7 +269,7 @@ export function useLogsStreamReader () {
const instance = new StreamReader<Log>({ url: logUrl, bufferLength: 200, token: apiInfo.secret, useWebsocket })
setItem({ key, instance })
if (oldInstance) {
if (oldInstance != null) {
oldInstance.destory()
}
@ -285,6 +285,6 @@ export function useConnectionStreamReader () {
const url = `${apiInfo.protocol}//${apiInfo.hostname}:${apiInfo.port}/connections`
return useMemo(
() => version.version ? new StreamReader<Snapshot>({ url, bufferLength: 200, token: apiInfo.secret, useWebsocket }) : null,
[apiInfo.secret, url, useWebsocket, version.version]
[apiInfo.secret, url, useWebsocket, version.version],
)
}

View File

@ -1,8 +1,8 @@
import { atom, useAtom } from "jotai";
import { isClashX, jsBridge } from "@lib/jsBridge";
import { atomWithStorage, useAtomValue } from "jotai/utils";
import { useLocation } from "react-use";
import { Client } from "@lib/request";
import { atom, useAtom } from 'jotai'
import { isClashX, jsBridge } from '@lib/jsBridge'
import { atomWithStorage, useAtomValue } from 'jotai/utils'
import { useLocation } from 'react-use'
import { Client } from '@lib/request'
const clashxConfigAtom = atom(async () => {
if (!isClashX()) {
@ -14,29 +14,29 @@ const clashxConfigAtom = atom(async () => {
hostname: info.host,
port: info.port,
secret: info.secret,
protocol: 'http:'
protocol: 'http:',
}
})
export const localStorageAtom = atomWithStorage<{
hostname: string;
port: string;
secret: string;
}[]>('externalControllers', [])
export const localStorageAtom = atomWithStorage<Array<{
hostname: string
port: string
secret: string
}>>('externalControllers', [])
export function useAPIInfo() {
export function useAPIInfo () {
const clashx = useAtomValue(clashxConfigAtom)
const location = useLocation()
const localStorage = useAtomValue(localStorageAtom)
if (clashx) {
if (clashx != null) {
return clashx
}
let url: URL | undefined;
let url: URL | undefined
{
const meta = document.querySelector<HTMLMetaElement>('meta[name="external-controller"]')
if (meta?.content?.match(/^https?:/)) {
if ((meta?.content?.match(/^https?:/)) != null) {
// [protocol]://[secret]@[hostname]:[port]
url = new URL(meta.content)
}
@ -54,15 +54,15 @@ export function useAPIInfo() {
const clientAtom = atom({
key: '',
instance: null as Client | null
instance: null as Client | null,
})
export function useClient() {
export function useClient () {
const {
hostname,
port,
secret,
protocol
protocol,
} = useAPIInfo()
const [item, setItem] = useAtom(clientAtom)

View File

@ -422,6 +422,16 @@
eslint-scope "^5.1.1"
eslint-utils "^3.0.0"
"@typescript-eslint/parser@^4.0.0":
version "4.28.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.28.1.tgz#5181b81658414f47291452c15bf6cd44a32f85bd"
integrity sha512-UjrMsgnhQIIK82hXGaD+MCN8IfORS1CbMdu7VlZbYa8LCZtbZjJA26De4IPQB7XYZbL8gJ99KWNj0l6WD0guJg==
dependencies:
"@typescript-eslint/scope-manager" "4.28.1"
"@typescript-eslint/types" "4.28.1"
"@typescript-eslint/typescript-estree" "4.28.1"
debug "^4.3.1"
"@typescript-eslint/parser@^4.28.0":
version "4.28.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.28.0.tgz#2404c16751a28616ef3abab77c8e51d680a12caa"
@ -440,11 +450,24 @@
"@typescript-eslint/types" "4.28.0"
"@typescript-eslint/visitor-keys" "4.28.0"
"@typescript-eslint/scope-manager@4.28.1":
version "4.28.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.28.1.tgz#fd3c20627cdc12933f6d98b386940d8d0ce8a991"
integrity sha512-o95bvGKfss6705x7jFGDyS7trAORTy57lwJ+VsYwil/lOUxKQ9tA7Suuq+ciMhJc/1qPwB3XE2DKh9wubW8YYA==
dependencies:
"@typescript-eslint/types" "4.28.1"
"@typescript-eslint/visitor-keys" "4.28.1"
"@typescript-eslint/types@4.28.0":
version "4.28.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.28.0.tgz#a33504e1ce7ac51fc39035f5fe6f15079d4dafb0"
integrity sha512-p16xMNKKoiJCVZY5PW/AfILw2xe1LfruTcfAKBj3a+wgNYP5I9ZEKNDOItoRt53p4EiPV6iRSICy8EPanG9ZVA==
"@typescript-eslint/types@4.28.1":
version "4.28.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.28.1.tgz#d0f2ecbef3684634db357b9bbfc97b94b828f83f"
integrity sha512-4z+knEihcyX7blAGi7O3Fm3O6YRCP+r56NJFMNGsmtdw+NCdpG5SgNz427LS9nQkRVTswZLhz484hakQwB8RRg==
"@typescript-eslint/typescript-estree@4.28.0":
version "4.28.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.0.tgz#e66d4e5aa2ede66fec8af434898fe61af10c71cf"
@ -458,6 +481,19 @@
semver "^7.3.5"
tsutils "^3.21.0"
"@typescript-eslint/typescript-estree@4.28.1":
version "4.28.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.1.tgz#af882ae41740d1f268e38b4d0fad21e7e8d86a81"
integrity sha512-GhKxmC4sHXxHGJv8e8egAZeTZ6HI4mLU6S7FUzvFOtsk7ZIDN1ksA9r9DyOgNqowA9yAtZXV0Uiap61bIO81FQ==
dependencies:
"@typescript-eslint/types" "4.28.1"
"@typescript-eslint/visitor-keys" "4.28.1"
debug "^4.3.1"
globby "^11.0.3"
is-glob "^4.0.1"
semver "^7.3.5"
tsutils "^3.21.0"
"@typescript-eslint/visitor-keys@4.28.0":
version "4.28.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.0.tgz#255c67c966ec294104169a6939d96f91c8a89434"
@ -466,6 +502,14 @@
"@typescript-eslint/types" "4.28.0"
eslint-visitor-keys "^2.0.0"
"@typescript-eslint/visitor-keys@4.28.1":
version "4.28.1"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.1.tgz#162a515ee255f18a6068edc26df793cdc1ec9157"
integrity sha512-K4HMrdFqr9PFquPu178SaSb92CaWe2yErXyPumc8cYWxFmhgJsNY9eSePmO05j0JhBvf2Cdhptd6E6Yv9HVHcg==
dependencies:
"@typescript-eslint/types" "4.28.1"
eslint-visitor-keys "^2.0.0"
"@vitejs/plugin-react-refresh@^1.3.3":
version "1.3.3"
resolved "https://registry.yarnpkg.com/@vitejs/plugin-react-refresh/-/plugin-react-refresh-1.3.3.tgz#d5afb3e0463f368a8afadfdd7305fe5c5fe78a6a"
@ -1016,6 +1060,19 @@ eslint-config-react-app@^6.0.0:
dependencies:
confusing-browser-globals "^1.0.10"
eslint-config-standard-with-typescript@^20.0.0:
version "20.0.0"
resolved "https://registry.yarnpkg.com/eslint-config-standard-with-typescript/-/eslint-config-standard-with-typescript-20.0.0.tgz#0c550eca0a216cbf8da9013eb6e311acd3102d87"
integrity sha512-IoySf3r0a2+P3Z6GMjv8p1HuOQ6GWQbMpdt9G8uEbkGpnNWAGBXpgaiutbZHbaQAvG5pkVtYepCfHUxYbVDLCA==
dependencies:
"@typescript-eslint/parser" "^4.0.0"
eslint-config-standard "^16.0.0"
eslint-config-standard@^16.0.0:
version "16.0.3"
resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-16.0.3.tgz#6c8761e544e96c531ff92642eeb87842b8488516"
integrity sha512-x4fmJL5hGqNJKGHSjnLdgA6U6h1YW/G2dW9fA+cyVur4SK6lyue8+UgNKWlZtUDTXvgKDD/Oa3GQjmB5kjtVvg==
eslint-import-resolver-node@^0.3.4:
version "0.3.4"
resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717"
@ -1032,6 +1089,14 @@ eslint-module-utils@^2.6.1:
debug "^3.2.7"
pkg-dir "^2.0.0"
eslint-plugin-es@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz#75a7cdfdccddc0589934aeeb384175f221c57893"
integrity sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==
dependencies:
eslint-utils "^2.0.0"
regexpp "^3.0.0"
eslint-plugin-flowtype@^5.7.2:
version "5.7.2"
resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-5.7.2.tgz#482a42fe5d15ee614652ed256d37543d584d7bc0"
@ -1078,6 +1143,23 @@ eslint-plugin-jsx-a11y@^6.4.1:
jsx-ast-utils "^3.1.0"
language-tags "^1.0.5"
eslint-plugin-node@^11.1.0:
version "11.1.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz#c95544416ee4ada26740a30474eefc5402dc671d"
integrity sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==
dependencies:
eslint-plugin-es "^3.0.0"
eslint-utils "^2.0.0"
ignore "^5.1.1"
minimatch "^3.0.4"
resolve "^1.10.1"
semver "^6.1.0"
eslint-plugin-promise@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-5.1.0.tgz#fb2188fb734e4557993733b41aa1a688f46c6f24"
integrity sha512-NGmI6BH5L12pl7ScQHbg7tvtk4wPxxj8yPHH47NvSmMtFneC077PSeY3huFj06ZWZvtbfxSPt3RuOQD5XcR4ng==
eslint-plugin-react-hooks@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.2.0.tgz#8c229c268d468956334c943bb45fc860280f5556"
@ -1109,7 +1191,7 @@ eslint-scope@^5.1.1:
esrecurse "^4.3.0"
estraverse "^4.1.1"
eslint-utils@^2.1.0:
eslint-utils@^2.0.0, eslint-utils@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27"
integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==
@ -1469,7 +1551,7 @@ ignore@^4.0.6:
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
ignore@^5.1.4:
ignore@^5.1.1, ignore@^5.1.4:
version "5.1.8"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57"
integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==
@ -2258,6 +2340,11 @@ regexp.prototype.flags@^1.3.1:
call-bind "^1.0.2"
define-properties "^1.1.3"
regexpp@^3.0.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==
regexpp@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2"
@ -2283,7 +2370,7 @@ resolve-pathname@^3.0.0:
resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd"
integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==
resolve@^1.10.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.20.0:
resolve@^1.10.0, resolve@^1.10.1, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.20.0:
version "1.20.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975"
integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==
@ -2362,7 +2449,7 @@ screenfull@^5.1.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
semver@^6.3.0:
semver@^6.1.0, semver@^6.3.0:
version "6.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==