From accef856fcab7f2f977c8bf490d1b5ee914f46d5 Mon Sep 17 00:00:00 2001 From: Michel Weststrate Date: Mon, 13 Dec 2021 05:46:42 -0800 Subject: [PATCH] Remove remaining Node imports from core Summary: Removed remaining path / fs imports from Flipper core. `expand-tide` needed replacement too, but noticed that it never actually rewrites paths since all use cases were already using absolute paths, so removed it instead. Reviewed By: aigoncharov Differential Revision: D33017654 fbshipit-source-id: e12f66ef68b5f9e4279411c94445a2fb87249e9a --- .../src/plugin/DevicePlugin.tsx | 2 +- .../src/chrome/ScreenCaptureButtons.tsx | 2 +- .../src/chrome/VideoRecordingButton.tsx | 2 +- .../src/chrome/settings/configFields.tsx | 15 +- .../src/deprecated-exports.tsx | 3 - .../src/devices/BaseDevice.tsx | 10 +- .../src/dispatcher/__tests__/plugins.node.tsx | 2 +- .../src/ui/components/FileList.tsx | 217 ------------------ desktop/flipper-ui-core/src/ui/index.tsx | 3 - .../flipper-ui-core/src/utils/exportData.tsx | 29 +-- .../flipper-ui-core/src/utils/fbEmployee.tsx | 7 +- .../flipper-ui-core/src/utils/screenshot.tsx | 43 +--- desktop/package.json | 2 - desktop/plugin-lib/package.json | 1 - desktop/plugin-lib/src/getSourcePlugins.ts | 2 - desktop/plugin-lib/src/pluginPaths.ts | 3 +- desktop/plugins/public/navigation/plugin.tsx | 5 +- desktop/yarn.lock | 5 - 18 files changed, 47 insertions(+), 306 deletions(-) delete mode 100644 desktop/flipper-ui-core/src/ui/components/FileList.tsx diff --git a/desktop/flipper-plugin/src/plugin/DevicePlugin.tsx b/desktop/flipper-plugin/src/plugin/DevicePlugin.tsx index 393830aaf..c655b5d6d 100644 --- a/desktop/flipper-plugin/src/plugin/DevicePlugin.tsx +++ b/desktop/flipper-plugin/src/plugin/DevicePlugin.tsx @@ -30,7 +30,7 @@ export interface Device { clearLogs(): Promise; sendMetroCommand(command: string): Promise; navigateToLocation(location: string): Promise; - screenshot(): Promise; + screenshot(): Promise; } export type DevicePluginPredicate = (device: Device) => boolean; diff --git a/desktop/flipper-ui-core/src/chrome/ScreenCaptureButtons.tsx b/desktop/flipper-ui-core/src/chrome/ScreenCaptureButtons.tsx index db303421e..317b9d489 100644 --- a/desktop/flipper-ui-core/src/chrome/ScreenCaptureButtons.tsx +++ b/desktop/flipper-ui-core/src/chrome/ScreenCaptureButtons.tsx @@ -9,11 +9,11 @@ import {Button as AntButton, message} from 'antd'; import React, {useState, useEffect, useCallback} from 'react'; -import path from 'path'; import {capture, getCaptureLocation, getFileName} from '../utils/screenshot'; import {CameraOutlined, VideoCameraOutlined} from '@ant-design/icons'; import {useStore} from '../utils/useStore'; import {getRenderHostInstance} from '../RenderHost'; +import {path} from 'flipper-plugin'; async function openFile(path: string) { getRenderHostInstance().flipperServer.exec('open-file', path); diff --git a/desktop/flipper-ui-core/src/chrome/VideoRecordingButton.tsx b/desktop/flipper-ui-core/src/chrome/VideoRecordingButton.tsx index cdada365f..1867eca37 100644 --- a/desktop/flipper-ui-core/src/chrome/VideoRecordingButton.tsx +++ b/desktop/flipper-ui-core/src/chrome/VideoRecordingButton.tsx @@ -10,8 +10,8 @@ import React, {Component} from 'react'; import BaseDevice from '../devices/BaseDevice'; import {Button, Glyph, colors} from '../ui'; -import path from 'path'; import {getRenderHostInstance} from '../RenderHost'; +import {path} from 'flipper-plugin'; type OwnProps = { recordingFinished: (path: string | null) => void; diff --git a/desktop/flipper-ui-core/src/chrome/settings/configFields.tsx b/desktop/flipper-ui-core/src/chrome/settings/configFields.tsx index 868095cb1..5cbe0c892 100644 --- a/desktop/flipper-ui-core/src/chrome/settings/configFields.tsx +++ b/desktop/flipper-ui-core/src/chrome/settings/configFields.tsx @@ -16,11 +16,9 @@ import { colors, Glyph, } from '../../ui'; -import React, {useState} from 'react'; -import {promises as fs} from 'fs'; -import {theme} from 'flipper-plugin'; +import React, {useState, useEffect} from 'react'; +import {getFlipperLib, theme} from 'flipper-plugin'; import {getRenderHostInstance} from '../../RenderHost'; -import {useEffect} from 'react'; export const ConfigFieldContainer = styled(FlexRow)({ paddingLeft: 10, @@ -75,8 +73,8 @@ export function FilePathConfigField(props: { useEffect(() => { (async function () { try { - const stat = await fs.stat(value); - setIsValid(props.isRegularFile !== stat.isDirectory()); + const stat = await getFlipperLib().remoteServerContext.fs.stat(value); + setIsValid(props.isRegularFile !== stat.isDirectory); } catch (_) { setIsValid(false); } @@ -93,8 +91,9 @@ export function FilePathConfigField(props: { onChange={(e) => { setValue(e.target.value); props.onChange(e.target.value); - fs.stat(e.target.value) - .then((stat) => stat.isDirectory()) + getFlipperLib() + .remoteServerContext.fs.stat(e.target.value) + .then((stat) => stat.isDirectory) .then((valid) => { if (valid !== isValid) { setIsValid(valid); diff --git a/desktop/flipper-ui-core/src/deprecated-exports.tsx b/desktop/flipper-ui-core/src/deprecated-exports.tsx index c3e0998c6..9b36400c8 100644 --- a/desktop/flipper-ui-core/src/deprecated-exports.tsx +++ b/desktop/flipper-ui-core/src/deprecated-exports.tsx @@ -27,7 +27,6 @@ export {PluginClient, Props, KeyboardActions} from './plugin'; export {default as Client} from './Client'; export {reportUsage} from 'flipper-common'; export {default as promiseTimeout} from './utils/promiseTimeout'; -export {bufferToBlob} from './utils/screenshot'; export {getPluginKey} from './utils/pluginKey'; export {Notification, Idler} from 'flipper-plugin'; export {IdlerImpl} from './utils/Idler'; @@ -72,8 +71,6 @@ export {default as Checkbox} from './ui/components/Checkbox'; export {default as Orderable} from './ui/components/Orderable'; export {Component, PureComponent} from 'react'; export {default as ContextMenu} from './ui/components/ContextMenu'; -export {FileListFiles} from './ui/components/FileList'; -export {default as FileList} from './ui/components/FileList'; export {default as View} from './ui/components/View'; export {default as Sidebar} from './ui/components/Sidebar'; export {default as FlexBox} from './ui/components/FlexBox'; diff --git a/desktop/flipper-ui-core/src/devices/BaseDevice.tsx b/desktop/flipper-ui-core/src/devices/BaseDevice.tsx index f2d42fc32..e5d228ff3 100644 --- a/desktop/flipper-ui-core/src/devices/BaseDevice.tsx +++ b/desktop/flipper-ui-core/src/devices/BaseDevice.tsx @@ -199,14 +199,12 @@ export default class BaseDevice implements Device { return this.flipperServer.exec('device-supports-screenshot', this.serial); } - async screenshot(): Promise { + async screenshot(): Promise { if (this.isArchived) { - return Buffer.from([]); + return new Uint8Array(); } - return Buffer.from( - Base64.toUint8Array( - await this.flipperServer.exec('device-take-screenshot', this.serial), - ), + return Base64.toUint8Array( + await this.flipperServer.exec('device-take-screenshot', this.serial), ); } diff --git a/desktop/flipper-ui-core/src/dispatcher/__tests__/plugins.node.tsx b/desktop/flipper-ui-core/src/dispatcher/__tests__/plugins.node.tsx index 64bb0ece6..781c035f1 100644 --- a/desktop/flipper-ui-core/src/dispatcher/__tests__/plugins.node.tsx +++ b/desktop/flipper-ui-core/src/dispatcher/__tests__/plugins.node.tsx @@ -16,13 +16,13 @@ import dispatcher, { getLatestCompatibleVersionOfEachPlugin, } from '../plugins'; import {BundledPluginDetails, InstalledPluginDetails} from 'flipper-common'; -import path from 'path'; import {createRootReducer, State} from '../../reducers/index'; import {getLogger} from 'flipper-common'; import configureStore from 'redux-mock-store'; import TestPlugin from './TestPlugin'; import {_SandyPluginDefinition} from 'flipper-plugin'; import {getRenderHostInstance} from '../../RenderHost'; +import path from 'path'; let loadDynamicPluginsMock: jest.Mock; diff --git a/desktop/flipper-ui-core/src/ui/components/FileList.tsx b/desktop/flipper-ui-core/src/ui/components/FileList.tsx deleted file mode 100644 index 364f897ac..000000000 --- a/desktop/flipper-ui-core/src/ui/components/FileList.tsx +++ /dev/null @@ -1,217 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - */ - -import {Component} from 'react'; -import path from 'path'; -import fs from 'fs'; - -const EMPTY_MAP = new Map(); -const EMPTY_FILE_LIST_STATE = {error: null, files: EMPTY_MAP}; - -export type FileListFileType = 'file' | 'folder'; - -export type FileListFile = { - name: string; - src: string; - type: FileListFileType; - size: number; - mtime: number; - atime: number; - ctime: number; - birthtime: number; -}; - -export type FileListFiles = Array; - -type FileListProps = { - /** Path to the folder */ - src: string; - /** Content to be rendered in case of an error */ - onError?: (err: Error) => React.ReactNode | null | undefined; - /** Content to be rendered while loading */ - onLoad?: () => void; - /** Content to be rendered when the file list is loaded */ - onFiles: (files: FileListFiles) => React.ReactNode; -}; - -type FileListState = { - files: Map; - error: Error | null | undefined; -}; - -/** - * List the contents of a folder from the user's file system. The file system is watched for - * changes and this list will automatically update. - */ -export default class FileList extends Component { - constructor(props: FileListProps, context: Object) { - super(props, context); - this.state = EMPTY_FILE_LIST_STATE; - } - - watcher: fs.FSWatcher | null | undefined; - - fetchFile(src: string, name: string): Promise { - return new Promise((resolve, reject) => { - const loc = path.join(src, name); - - fs.lstat(loc, (err, stat) => { - if (err) { - reject(err); - } else { - const details: FileListFile = { - atime: Number(stat.atime), - birthtime: - typeof stat.birthtime === 'object' ? Number(stat.birthtime) : 0, - ctime: Number(stat.ctime), - mtime: Number(stat.mtime), - name, - size: stat.size, - src: loc, - type: stat.isDirectory() ? 'folder' : 'file', - }; - resolve(details); - } - }); - }); - } - - fetchFilesFromFolder( - originalSrc: string, - currentSrc: string, - callback: Function, - ) { - const hasChangedDir = () => this.props.src !== originalSrc; - - let filesSet: Map = new Map(); - fs.readdir(currentSrc, (err, files) => { - if (err) { - return callback(err, EMPTY_MAP); - } - let remainingPaths = files.length; - - const next = () => { - if (hasChangedDir()) { - return callback(null, EMPTY_MAP); - } - - if (!remainingPaths) { - return callback(null, filesSet); - } - - const name = files.shift(); - if (name) { - this.fetchFile(currentSrc, name) - .then((data) => { - filesSet.set(name, data); - if (data.type == 'folder') { - this.fetchFilesFromFolder( - originalSrc, - path.join(currentSrc, name), - function (err: Error, files: Map) { - if (err) { - return callback(err, EMPTY_MAP); - } - filesSet = new Map([...filesSet, ...files]); - remainingPaths--; - if (!remainingPaths) { - return callback(null, filesSet); - } - }, - ); - } else { - remainingPaths--; - } - next(); - }) - .catch((err) => { - return callback(err, EMPTY_MAP); - }); - } - }; - - next(); - }); - } - - fetchFiles(callback?: Function) { - const {src} = this.props; - - const setState = (data: FileListState) => { - if (!hasChangedDir()) { - this.setState(data); - } - }; - - const hasChangedDir = () => this.props.src !== src; - - this.fetchFilesFromFolder( - src, - src, - function (err: Error, files: Map) { - setState({error: err, files: files}); - if (callback) { - callback(); - } - }, - ); - } - - UNSAFE_componentWillReceiveProps(nextProps: FileListProps) { - if (nextProps.src !== this.props.src) { - this.initialFetch(nextProps); - } - } - - componentDidMount() { - this.initialFetch(this.props); - } - - componentWillUnmount() { - this.removeWatcher(); - } - - initialFetch(props: FileListProps) { - this.removeWatcher(); - - fs.access(props.src, fs.constants.R_OK, (err) => { - if (err) { - this.setState({error: err, files: EMPTY_MAP}); - return; - } - - this.fetchFiles(props.onLoad); - - this.watcher = fs.watch(props.src, () => { - this.fetchFiles(); - }); - - this.watcher.on('error', (err) => { - this.setState({error: err, files: EMPTY_MAP}); - this.removeWatcher(); - }); - }); - } - - removeWatcher() { - if (this.watcher) { - this.watcher.close(); - } - } - - render() { - const {error, files} = this.state; - const {onError, onFiles} = this.props; - if (error && onError) { - return onError(error); - } else { - return onFiles(Array.from(files.values())); - } - } -} diff --git a/desktop/flipper-ui-core/src/ui/index.tsx b/desktop/flipper-ui-core/src/ui/index.tsx index 2c4f04d89..6cf459182 100644 --- a/desktop/flipper-ui-core/src/ui/index.tsx +++ b/desktop/flipper-ui-core/src/ui/index.tsx @@ -62,9 +62,6 @@ export {Component, PureComponent} from 'react'; // context menus and dropdowns export {default as ContextMenu} from './components/ContextMenu'; -// file -export {FileListFile, FileListFiles} from './components/FileList'; -export {default as FileList} from './components/FileList'; // utility elements export {default as View} from './components/View'; export {default as Sidebar} from './components/Sidebar'; diff --git a/desktop/flipper-ui-core/src/utils/exportData.tsx b/desktop/flipper-ui-core/src/utils/exportData.tsx index 46c5af743..47cf1793a 100644 --- a/desktop/flipper-ui-core/src/utils/exportData.tsx +++ b/desktop/flipper-ui-core/src/utils/exportData.tsx @@ -8,7 +8,6 @@ */ import * as React from 'react'; -import path from 'path'; import {getLogger} from 'flipper-common'; import {Store, MiddlewareAPI} from '../reducers'; import {DeviceExport} from '../devices/BaseDevice'; @@ -20,7 +19,6 @@ import {pluginKey} from '../utils/pluginKey'; import {DevicePluginMap, ClientPluginMap} from '../plugin'; import {default as BaseDevice} from '../devices/BaseDevice'; import {default as ArchivedDevice} from '../devices/ArchivedDevice'; -import fs from 'fs-extra'; import {v4 as uuidv4} from 'uuid'; import {tryCatchReportPlatformFailures} from 'flipper-common'; import {TestIdler} from './Idler'; @@ -33,7 +31,7 @@ import {deconstructClientId} from 'flipper-common'; import {processMessageQueue} from './messageQueue'; import {getPluginTitle} from './pluginUtils'; import {capture} from './screenshot'; -import {Dialog, Idler} from 'flipper-plugin'; +import {Dialog, getFlipperLib, Idler, path} from 'flipper-plugin'; import {ClientQuery} from 'flipper-common'; import ShareSheetExportUrl from '../chrome/ShareSheetExportUrl'; import ShareSheetExportFile from '../chrome/ShareSheetExportFile'; @@ -521,7 +519,10 @@ export const exportStoreToFile = ( }> => { return exportStore(store, includeSupportDetails, idler, statusUpdate).then( async ({serializedString, fetchMetaDataErrors}) => { - await fs.writeFile(exportFilePath, serializedString); + await getFlipperLib().remoteServerContext.fs.writeFile( + exportFilePath, + serializedString, + ); store.dispatch(resetSupportFormV2State()); return {fetchMetaDataErrors}; }, @@ -584,17 +585,17 @@ export function importDataToStore(source: string, data: string, store: Store) { } } -export const importFileToStore = (file: string, store: Store) => { - fs.readFile(file, 'utf8', (err, data) => { - if (err) { - console.error( - `[exportData] importFileToStore for file ${file} failed:`, - err, - ); - return; - } +export const importFileToStore = async (file: string, store: Store) => { + try { + const data = await getFlipperLib().remoteServerContext.fs.readFile(file); importDataToStore(file, data, store); - }); + } catch (err) { + console.error( + `[exportData] importFileToStore for file ${file} failed:`, + err, + ); + return; + } }; export function canOpenDialog() { diff --git a/desktop/flipper-ui-core/src/utils/fbEmployee.tsx b/desktop/flipper-ui-core/src/utils/fbEmployee.tsx index c1bd3df1e..4cde613ef 100644 --- a/desktop/flipper-ui-core/src/utils/fbEmployee.tsx +++ b/desktop/flipper-ui-core/src/utils/fbEmployee.tsx @@ -7,15 +7,14 @@ * @format */ -import util from 'util'; -import {exec as execImport} from 'child_process'; +import {getFlipperLib} from 'flipper-plugin'; const cmd = 'klist --json'; const endWith = '@THEFACEBOOK.COM'; export async function isFBEmployee(): Promise { - return util - .promisify(execImport)(cmd) + return getFlipperLib() + .remoteServerContext.childProcess.exec(cmd) .then( (stdobj: {stderr: string; stdout: string}) => { const principal = String(JSON.parse(stdobj.stdout).principal); diff --git a/desktop/flipper-ui-core/src/utils/screenshot.tsx b/desktop/flipper-ui-core/src/utils/screenshot.tsx index 9fb92e5d3..332cbe4f8 100644 --- a/desktop/flipper-ui-core/src/utils/screenshot.tsx +++ b/desktop/flipper-ui-core/src/utils/screenshot.tsx @@ -7,17 +7,15 @@ * @format */ -import fs from 'fs'; -import path from 'path'; import BaseDevice from '../devices/BaseDevice'; import {reportPlatformFailures} from 'flipper-common'; -import expandTilde from 'expand-tilde'; import {getRenderHostInstance} from '../RenderHost'; +import {getFlipperLib, path} from 'flipper-plugin'; export function getCaptureLocation() { - return expandTilde( + return ( getRenderHostInstance().serverConfig.processConfig.screenCapturePath || - getRenderHostInstance().serverConfig.paths.desktopPath, + getRenderHostInstance().serverConfig.paths.desktopPath ); } @@ -34,33 +32,14 @@ export async function capture(device: BaseDevice): Promise { } const pngPath = path.join(getCaptureLocation(), getFileName('png')); return reportPlatformFailures( - device.screenshot().then((buffer) => writeBufferToFile(pngPath, buffer)), + // TODO: there is no reason to read the screenshot first, grab it over the websocket, than send it back + // again to write in a file, probably easier to change screenshot api to `device.screenshot(): path` + device + .screenshot() + .then((buffer) => + getFlipperLib().remoteServerContext.fs.writeFileBinary(pngPath, buffer), + ) + .then(() => pngPath), 'captureScreenshot', ); } - -/** - * Writes a buffer to a specified file path. - * Returns a Promise which resolves to the file path. - */ -export const writeBufferToFile = ( - filePath: string, - buffer: Buffer, -): Promise => { - return new Promise((resolve, reject) => { - fs.writeFile(filePath, buffer, (err) => { - if (err) { - reject(err); - } else { - resolve(filePath); - } - }); - }); -}; - -/** - * Creates a Blob from a Buffer - */ -export const bufferToBlob = (buffer: Buffer): Blob => { - return new Blob([buffer]); -}; diff --git a/desktop/package.json b/desktop/package.json index 689cd8db9..6cc11c01c 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -83,7 +83,6 @@ "@types/deep-equal": "^1.0.1", "@types/detect-port": "^1.3.1", "@types/electron-devtools-installer": "^2.2.0", - "@types/expand-tilde": "^2.0.0", "@types/express": "^4.17.13", "@types/fb-watchman": "^2.0.1", "@types/form-data": "^2.2.1", @@ -150,7 +149,6 @@ "eslint-plugin-react": "^7.27.1", "eslint-plugin-react-hooks": "^4.3.0", "eslint-plugin-rulesdir": "^0.2.1", - "expand-tilde": "^2.0.2", "express": "^4.15.2", "fb-watchman": "^2.0.1", "flipper-babel-transformer": "0.0.0", diff --git a/desktop/plugin-lib/package.json b/desktop/plugin-lib/package.json index e5f3c2ff6..343868908 100644 --- a/desktop/plugin-lib/package.json +++ b/desktop/plugin-lib/package.json @@ -13,7 +13,6 @@ "decompress": "^4.2.1", "decompress-targz": "^4.1.1", "decompress-unzip": "^4.0.1", - "expand-tilde": "^2.0.2", "flipper-common": "^0.0.0", "fs-extra": "^10.0.0", "live-plugin-manager": "^0.17.0", diff --git a/desktop/plugin-lib/src/getSourcePlugins.ts b/desktop/plugin-lib/src/getSourcePlugins.ts index 2f9a452a2..a2b9b7eb7 100644 --- a/desktop/plugin-lib/src/getSourcePlugins.ts +++ b/desktop/plugin-lib/src/getSourcePlugins.ts @@ -9,7 +9,6 @@ import path from 'path'; import fs from 'fs-extra'; -import expandTilde from 'expand-tilde'; import {getPluginSourceFolders} from './pluginPaths'; import pmap from 'p-map'; import pfilter from 'p-filter'; @@ -49,7 +48,6 @@ export async function getSourcePlugins(): Promise { async function entryPointForPluginFolder( pluginsDir: string, ): Promise<{[key: string]: InstalledPluginDetails}> { - pluginsDir = expandTilde(pluginsDir); if (!(await fs.pathExists(pluginsDir))) { return {}; } diff --git a/desktop/plugin-lib/src/pluginPaths.ts b/desktop/plugin-lib/src/pluginPaths.ts index e0338f4bb..724e7c5a0 100644 --- a/desktop/plugin-lib/src/pluginPaths.ts +++ b/desktop/plugin-lib/src/pluginPaths.ts @@ -11,7 +11,6 @@ import path from 'path'; import {homedir} from 'os'; import fs from 'fs-extra'; import pFilter from 'p-filter'; -import expandTilde from 'expand-tilde'; const flipperDataDir = path.join(homedir(), '.flipper'); @@ -44,7 +43,7 @@ export async function getPluginSourceFolders(): Promise { } pluginFolders.push(path.resolve(__dirname, '..', '..', 'plugins', 'public')); pluginFolders.push(path.resolve(__dirname, '..', '..', 'plugins', 'fb')); - return pFilter(pluginFolders.map(expandTilde), (p) => fs.pathExists(p)); + return pFilter(pluginFolders, (p) => fs.pathExists(p)); } export function getPluginInstallationDir(name: string) { diff --git a/desktop/plugins/public/navigation/plugin.tsx b/desktop/plugins/public/navigation/plugin.tsx index 6551e37ab..18c651b6b 100644 --- a/desktop/plugins/public/navigation/plugin.tsx +++ b/desktop/plugins/public/navigation/plugin.tsx @@ -8,7 +8,6 @@ * @flow strict-local */ -import {bufferToBlob} from 'flipper'; import {RequiredParametersDialog} from './components'; import { removeBookmarkFromDB, @@ -73,14 +72,14 @@ export function plugin(client: PluginClient) { draft.unshift(navigationEvent); }); - const screenshot: Buffer = await client.device.screenshot(); + const screenshot = await client.device.screenshot(); if (screenshot.byteLength === 0) { console.warn( '[navigation] Could not retrieve valid screenshot from the device.', ); return; } - const blobURL = URL.createObjectURL(bufferToBlob(screenshot)); + const blobURL = URL.createObjectURL(new Blob([screenshot.buffer])); // this process is async, make sure we update the correct one.. const navigationEventIndex = navigationEvents .get() diff --git a/desktop/yarn.lock b/desktop/yarn.lock index cc7d8617c..bda46273a 100644 --- a/desktop/yarn.lock +++ b/desktop/yarn.lock @@ -2620,11 +2620,6 @@ resolved "https://registry.yarnpkg.com/@types/electron-devtools-installer/-/electron-devtools-installer-2.2.0.tgz#32ee4ebbe99b3daf9847a6d2097dc00b5de94f10" integrity sha512-HJNxpaOXuykCK4rQ6FOMxAA0NLFYsf7FiPFGmab0iQmtVBHSAfxzy3MRFpLTTDDWbV0yD2YsHOQvdu8yCqtCfw== -"@types/expand-tilde@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@types/expand-tilde/-/expand-tilde-2.0.0.tgz#c01a706675b9d60931bf6a7dc7dfa45d63540c97" - integrity sha512-17h/6MRHoetV2QVUVnUfrmaFCXNIFJ3uDJmXlklX2xDtlEb1W0OXLgP+qwND2Ibg/PtQfQi0vx19KGuPayjLiw== - "@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.18": version "4.17.19" resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.19.tgz#00acfc1632e729acac4f1530e9e16f6dd1508a1d"