From 0dfc73da93ff7579efbdfd9d7c45336d8a280120 Mon Sep 17 00:00:00 2001 From: Michel Weststrate Date: Wed, 8 Dec 2021 04:25:28 -0800 Subject: [PATCH] setup webserver for flipper-server Summary: This sets up the metro bundler for flipper-server, to be able to serve the front end. Note that this is a setup that is only relevant for development purposes Done in this diff: * setup metro * setup fast refresh * setup nodemon to be able to refresh on server changes Not done in this diff * Setup FlipperServerImpl in the flipper-server * Load flipper-ui-core in flipper-ui-browser * Load plugins * Support options, env vars etc etc * Make flipper-server stand alone (it is largely self contained, but still requires some static resources like theming) Reviewed By: passy, aigoncharov Differential Revision: D32626137 fbshipit-source-id: 47f580356ddf0993392d3b583082b187661727e9 --- desktop/flipper-server/package.json | 20 +- desktop/flipper-server/src/index.tsx | 13 +- .../flipper-server/src/startWebServerDev.tsx | 165 +++++++++++ desktop/flipper-server/tsconfig.json | 3 + desktop/flipper-ui-browser/package.json | 7 +- desktop/flipper-ui-browser/src/HMRClient.tsx | 277 ++++++++++++++++++ .../src/index-fast-refresh.tsx | 53 ++++ desktop/flipper-ui-browser/src/index.tsx | 4 +- desktop/static/index.web.dev.html | 117 ++++++++ desktop/yarn.lock | 118 +++++++- 10 files changed, 759 insertions(+), 18 deletions(-) create mode 100644 desktop/flipper-server/src/startWebServerDev.tsx create mode 100644 desktop/flipper-ui-browser/src/HMRClient.tsx create mode 100644 desktop/flipper-ui-browser/src/index-fast-refresh.tsx create mode 100644 desktop/static/index.web.dev.html diff --git a/desktop/flipper-server/package.json b/desktop/flipper-server/package.json index 2c1be5870..776fae76e 100644 --- a/desktop/flipper-server/package.json +++ b/desktop/flipper-server/package.json @@ -9,13 +9,27 @@ "types": "lib/index.d.ts", "license": "MIT", "bugs": "https://github.com/facebook/flipper/issues", - "dependencies": {}, - "devDependencies": {}, + "dependencies": { + "chalk": "^4.1.2", + "express": "^4.15.2", + "flipper-pkg-lib": "0.0.0", + "fs-extra": "^9.0.0", + "p-filter": "^2.1.0", + "socket.io": "^4.3.1" + }, + "devDependencies": { + "@types/express": "^4.17.13", + "metro": "^0.66.2", + "nodemon": "^2.0.15", + "ts-node": "^9.1.1", + "typescript": "^4.4.4" + }, "peerDependencies": {}, "scripts": { "reset": "rimraf lib *.tsbuildinfo", "build": "tsc -b", - "prepack": "yarn reset && yarn build" + "prepack": "yarn reset && yarn build", + "start": "cross-env NODE_ENV=development nodemon --watch './src/*.tsx' --exec '../ts-node' src/index.tsx" }, "files": [ "lib/**/*" diff --git a/desktop/flipper-server/src/index.tsx b/desktop/flipper-server/src/index.tsx index 90a375a48..dba7e8b0b 100644 --- a/desktop/flipper-server/src/index.tsx +++ b/desktop/flipper-server/src/index.tsx @@ -7,6 +7,13 @@ * @format */ -export function helloWorld() { - return true; -} +// TODO: currently flipper-server is only suitable for development, +// needs to be come independently runnable, prebundled, distributed, etc! +// in future require conditionally +import {startWebServerDev} from './startWebServerDev'; +import chalk from 'chalk'; + +startWebServerDev().catch((e) => { + console.error(chalk.red('Server error: '), e); + process.exit(1); +}); diff --git a/desktop/flipper-server/src/startWebServerDev.tsx b/desktop/flipper-server/src/startWebServerDev.tsx new file mode 100644 index 000000000..2cde4311a --- /dev/null +++ b/desktop/flipper-server/src/startWebServerDev.tsx @@ -0,0 +1,165 @@ +/** + * 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 {hostname} from 'os'; +import chalk from 'chalk'; +import express, {Express} from 'express'; +import http from 'http'; +import path from 'path'; +import fs from 'fs-extra'; +import socketio from 'socket.io'; +import {getWatchFolders} from 'flipper-pkg-lib'; +import Metro from 'metro'; +import pFilter from 'p-filter'; + +const PORT = 52342; +const rootDir = path.resolve(__dirname, '..', '..'); +const staticDir = path.join(rootDir, 'static'); +const babelTransformationsDir = path.resolve( + rootDir, + 'babel-transformer', + 'src', +); + +const uiSourceDirs = [ + 'flipper-ui-browser', + 'flipper-ui-core', + 'flipper-plugin', + 'flipper-common', +]; + +// This file is heavily inspired by scripts/start-dev-server.ts! +export async function startWebServerDev() { + checkDevServer(); + // await prepareDefaultPlugins( + // process.env.FLIPPER_RELEASE_CHANNEL === 'insiders', + // ); + // await ensurePluginFoldersWatchable(); + const {app, server} = await startAssetServer(PORT); + const socket = await addWebsocket(server); + await startMetroServer(app, server, socket); + // await compileMain(); + // if (dotenv && dotenv.parsed) { + // console.log('✅ Loaded env vars from .env file: ', dotenv.parsed); + // } + // shutdownElectron = launchElectron(port); + console.log( + `Flipper DEV server started at http://localhost:${PORT}/index.web.dev.html`, + ); +} + +function looksLikeDevServer(): boolean { + const hn = hostname(); + if (/^devvm.*\.facebook\.com$/.test(hn)) { + return true; + } + if (hn.endsWith('.od.fbinfra.net')) { + return true; + } + return false; +} + +function checkDevServer() { + if (looksLikeDevServer()) { + console.log( + chalk.red( + `✖ It looks like you're trying to start Flipper on your OnDemand or DevServer, which is not supported. Please run this in a local checkout on your laptop or desktop instead.`, + ), + ); + } +} + +function startAssetServer( + port: number, +): Promise<{app: Express; server: http.Server}> { + const app = express(); + + app.use((_req, res, next) => { + res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate'); + res.header('Expires', '-1'); + res.header('Pragma', 'no-cache'); + next(); + }); + + app.get('/', (_req, res) => { + fs.readFile(path.join(staticDir, 'index.web.dev.html'), (_err, content) => { + res.end(content); + }); + }); + + app.use(express.static(staticDir)); + + const server = http.createServer(app); + + return new Promise((resolve) => { + server.listen(port, 'localhost', () => resolve({app, server})); + }); +} + +async function addWebsocket(server: http.Server) { + const io = new socketio.Server(server); // 3.1.0 socket.io doesn't have type definitions + + io.on('connection', (client) => { + console.log(chalk.green(`Client connected ${client.id}`)); + }); + + // Refresh the app on changes. + // When Fast Refresh enabled, reloads are performed by HMRClient, so don't need to watch manually here. + // if (!process.env.FLIPPER_FAST_REFRESH) { + // await startWatchChanges(io); + // } + + return io; +} + +async function startMetroServer( + app: Express, + server: http.Server, + socket: socketio.Server, +) { + const watchFolders = await dedupeFolders( + ( + await Promise.all( + uiSourceDirs.map((dir) => getWatchFolders(path.resolve(rootDir, dir))), + ) + ).flat(), + ); + // console.log('Source dirs\n\t' + watchFolders.join('\n\t')); + const baseConfig = await Metro.loadConfig(); + const config = Object.assign({}, baseConfig, { + projectRoot: rootDir, + watchFolders, + transformer: { + ...baseConfig.transformer, + babelTransformerPath: path.join(babelTransformationsDir, 'transform-app'), + }, + resolver: { + ...baseConfig.resolver, + resolverMainFields: ['flipperBundlerEntry', 'module', 'main'], + blacklistRE: /\.native\.js$/, + sourceExts: ['js', 'jsx', 'ts', 'tsx', 'json', 'mjs', 'cjs'], + }, + watch: true, + }); + const connectMiddleware = await Metro.createConnectMiddleware(config); + app.use(connectMiddleware.middleware); + connectMiddleware.attachHmrServer(server); + app.use(function (err: any, _req: any, _res: any, next: any) { + console.error(chalk.red('\n\nCompile error in client bundle\n'), err); + socket.local.emit('hasErrors', err.toString()); + next(); + }); +} + +async function dedupeFolders(paths: string[]): Promise { + return pFilter( + paths.filter((value, index, self) => self.indexOf(value) === index), + (f) => fs.pathExists(f), + ); +} diff --git a/desktop/flipper-server/tsconfig.json b/desktop/flipper-server/tsconfig.json index 9161fe7a6..69d14c891 100644 --- a/desktop/flipper-server/tsconfig.json +++ b/desktop/flipper-server/tsconfig.json @@ -7,6 +7,9 @@ "references": [ { "path": "../flipper-server-core" + }, + { + "path": "../pkg-lib" } ] } diff --git a/desktop/flipper-ui-browser/package.json b/desktop/flipper-ui-browser/package.json index 2ff886336..cd568a8cb 100644 --- a/desktop/flipper-ui-browser/package.json +++ b/desktop/flipper-ui-browser/package.json @@ -10,7 +10,12 @@ "license": "MIT", "bugs": "https://github.com/facebook/flipper/issues", "dependencies": {}, - "devDependencies": {}, + "devDependencies": { + "invariant": "^2.2.4", + "metro-runtime": "^0.66.2", + "pretty-format": "^27.3.1", + "react-refresh": "^0.10.0" + }, "peerDependencies": {}, "scripts": { "reset": "rimraf lib *.tsbuildinfo", diff --git a/desktop/flipper-ui-browser/src/HMRClient.tsx b/desktop/flipper-ui-browser/src/HMRClient.tsx new file mode 100644 index 000000000..9d0bb5dde --- /dev/null +++ b/desktop/flipper-ui-browser/src/HMRClient.tsx @@ -0,0 +1,277 @@ +/** + * 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 + */ + +/** + * * * * * * * ** * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * * * * * ** * * * * * * * * * + * This implementation of HMR Client is based on React Native implementation with some code commented out: * + * https://github.com/facebook/react-native/blob/master/Libraries/Utilities/HMRClient.js * + * * * * * * * ** * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * * * * * ** * * * * * * * * * + */ + +// // const DevSettings = require('./DevSettings'); +import invariant from 'invariant'; +// @ts-ignore +import {default as MetroHMRClient} from 'metro-runtime/src/modules/HMRClient'; +// // const Platform = require('./Platform'); +import prettyFormat from 'pretty-format'; + +const pendingEntryPoints: string[] = []; +let hmrClient: any = null; +let hmrUnavailableReason: null | string = null; +let currentCompileErrorMessage: null | string = null; +let didConnect = false; +const pendingLogs: [string, any][] = []; + +/** + * HMR Client that receives from the server HMR updates and propagates them + * runtime to reflects those changes. + */ +const HMRClient = { + enable() { + if (hmrUnavailableReason !== null) { + // If HMR became unavailable while you weren't using it, + // explain why when you try to turn it on. + // This is an error (and not a warning) because it is shown + // in response to a direct user action. + throw new Error(hmrUnavailableReason); + } + + invariant(hmrClient, 'Expected HMRClient.setup() call at startup.'); + //// const LoadingView = require('./LoadingView'); + + // We use this for internal logging only. + // It doesn't affect the logic. + hmrClient.send(JSON.stringify({type: 'log-opt-in'})); + + // When toggling Fast Refresh on, we might already have some stashed updates. + // Since they'll get applied now, we'll show a banner. + const hasUpdates = hmrClient.hasPendingUpdates(); + + if (hasUpdates) { + //// LoadingView.showMessage('Refreshing...', 'refresh'); + console.log('Loading start: Refreshing...'); + } + try { + hmrClient.enable(); + } finally { + if (hasUpdates) { + //// LoadingView.hide(); + console.log('Loading end'); + } + } + + // There could be a compile error while Fast Refresh was off, + // but we ignored it at the time. Show it now. + showCompileError(); + }, + + disable() { + invariant(hmrClient, 'Expected HMRClient.setup() call at startup.'); + hmrClient.disable(); + }, + + registerBundle(requestUrl: string) { + invariant(hmrClient, 'Expected HMRClient.setup() call at startup.'); + pendingEntryPoints.push(requestUrl); + registerBundleEntryPoints(hmrClient); + }, + + log(level: string, data: any) { + if (!hmrClient) { + // Catch a reasonable number of early logs + // in case hmrClient gets initialized later. + pendingLogs.push([level, data]); + if (pendingLogs.length > 100) { + pendingLogs.shift(); + } + return; + } + try { + hmrClient.send( + JSON.stringify({ + type: 'log', + level, + data: data.map((item: any) => + typeof item === 'string' + ? item + : prettyFormat(item, { + escapeString: true, + highlight: true, + maxDepth: 3, + min: true, + plugins: [(prettyFormat as any).plugins.ReactElement], + }), + ), + }), + ); + } catch (error) { + // If sending logs causes any failures we want to silently ignore them + // to ensure we do not cause infinite-logging loops. + } + }, + + // Called once by the bridge on startup, even if Fast Refresh is off. + // It creates the HMR client but doesn't actually set up the socket yet. + setup( + platform: string, + bundleEntry: string, + host: string, + port: string, + isEnabled: boolean, + ) { + invariant(platform, 'Missing required parameter `platform`'); + invariant(bundleEntry, 'Missing required parameter `bundleEntry`'); + invariant(host, 'Missing required parameter `host`'); + invariant(!hmrClient, 'Cannot initialize hmrClient twice'); + + //// const LoadingView = require('./LoadingView'); + + const wsHost = port !== null && port !== '' ? `${host}:${port}` : host; + const client = new MetroHMRClient(`ws://${wsHost}/hot`); + hmrClient = client; + + pendingEntryPoints.push( + `ws://${wsHost}/hot?bundleEntry=${bundleEntry}&platform=${platform}`, + ); + + client.on('connection-error', (e: any) => { + let error = `Cannot connect to the Metro server. +Try the following to fix the issue: +- Ensure that the Metro server is running and available on the same network`; + + error += ` +- Ensure that your device/emulator is connected to your machine and has USB debugging enabled - run 'adb devices' to see a list of connected devices +- If you're on a physical device connected to the same machine, run 'adb reverse tcp:8081 tcp:8081' to forward requests from your device +- If your device is on the same Wi-Fi network, set 'Debug server host & port for device' in 'Dev settings' to your machine's IP address and the port of the local dev server - e.g. 10.0.1.1:8081`; + + error += ` +URL: ${host}:${port} +Error: ${e.message}`; + + setHMRUnavailableReason(error); + }); + + client.on('update-start', ({isInitialUpdate}: any) => { + currentCompileErrorMessage = null; + didConnect = true; + + if (client.isEnabled() && !isInitialUpdate) { + //// LoadingView.showMessage('Refreshing...', 'refresh'); + console.log('Loading start: Refreshing...'); + } + }); + + client.on('update', () => {}); + + client.on('update-done', () => { + //// LoadingView.hide(); + console.log('Loading end'); + }); + + client.on('error', (data: any) => { + //// LoadingView.hide(); + console.log('Loading end'); + + if (data.type === 'GraphNotFoundError') { + client.close(); + setHMRUnavailableReason( + 'The Metro server has restarted since the last edit. Reload to reconnect.', + ); + } else if (data.type === 'RevisionNotFoundError') { + client.close(); + setHMRUnavailableReason( + 'The Metro server and the client are out of sync. Reload to reconnect.', + ); + } else { + currentCompileErrorMessage = `${data.type} ${data.message}`; + if (client.isEnabled()) { + showCompileError(); + } + } + }); + + client.on('close', (_data: any) => { + //// LoadingView.hide(); + console.log('Loading end'); + setHMRUnavailableReason('Disconnected from the Metro server.'); + }); + + if (isEnabled) { + HMRClient.enable(); + } else { + HMRClient.disable(); + } + + registerBundleEntryPoints(hmrClient); + flushEarlyLogs(hmrClient); + }, +}; + +function setHMRUnavailableReason(reason: string) { + invariant(hmrClient, 'Expected HMRClient.setup() call at startup.'); + if (hmrUnavailableReason !== null) { + // Don't show more than one warning. + return; + } + hmrUnavailableReason = reason; + + // We only want to show a warning if Fast Refresh is on *and* if we ever + // previously managed to connect successfully. We don't want to show + // the warning to native engineers who use cached bundles without Metro. + if (hmrClient.isEnabled() && didConnect) { + console.warn(reason); + // (Not using the `warning` module to prevent a Buck cycle.) + } +} + +function registerBundleEntryPoints(client: any) { + if (hmrUnavailableReason) { + // // DevSettings.reload('Bundle Splitting – Metro disconnected'); + console.log('Bundle Spliiting - Metro disconnected'); + return; + } + + if (pendingEntryPoints.length > 0) { + client.send( + JSON.stringify({ + type: 'register-entrypoints', + entryPoints: pendingEntryPoints, + }), + ); + pendingEntryPoints.length = 0; + } +} + +function flushEarlyLogs(_client: any) { + try { + pendingLogs.forEach(([level, data]) => { + HMRClient.log(level, data); + }); + } finally { + pendingLogs.length = 0; + } +} + +function showCompileError() { + if (currentCompileErrorMessage === null) { + return; + } + + const message = currentCompileErrorMessage; + currentCompileErrorMessage = null; + + const error = new Error(message); + // Symbolicating compile errors is wasted effort + // because the stack trace is meaningless: + (error as any).preventSymbolication = true; + (window as any).flipperShowError?.(message); + throw error; +} + +export default HMRClient; diff --git a/desktop/flipper-ui-browser/src/index-fast-refresh.tsx b/desktop/flipper-ui-browser/src/index-fast-refresh.tsx new file mode 100644 index 000000000..6682d2720 --- /dev/null +++ b/desktop/flipper-ui-browser/src/index-fast-refresh.tsx @@ -0,0 +1,53 @@ +/** + * 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 {default as HmrClient} from './HMRClient'; +// @ts-ignore +import {default as ReactRefreshRuntime} from 'react-refresh/runtime'; + +HmrClient.setup( + 'web', + (window as any).flipperConfig.entryPoint, + 'localhost', + window.location.port, + true, +); + +ReactRefreshRuntime.injectIntoGlobalHook(window); + +const Refresh = { + performFullRefresh(reason: string) { + console.log('Perform full refresh', reason); + window.location.reload(); + }, + + createSignatureFunctionForTransform: + ReactRefreshRuntime.createSignatureFunctionForTransform, + + isLikelyComponentType: ReactRefreshRuntime.isLikelyComponentType, + + getFamilyByType: ReactRefreshRuntime.getFamilyByType, + + register: ReactRefreshRuntime.register, + + performReactRefresh() { + if (ReactRefreshRuntime.hasUnrecoverableErrors()) { + console.error('Fast refresh - Unrecolverable'); + window.location.reload(); + return; + } + ReactRefreshRuntime.performReactRefresh(); + console.log('Perform react refresh'); + }, +}; + +(require as any).Refresh = Refresh; + +// eslint-disable-next-line import/no-commonjs +require('./index.tsx'); diff --git a/desktop/flipper-ui-browser/src/index.tsx b/desktop/flipper-ui-browser/src/index.tsx index 90a375a48..2ff8bb5ac 100644 --- a/desktop/flipper-ui-browser/src/index.tsx +++ b/desktop/flipper-ui-browser/src/index.tsx @@ -7,6 +7,4 @@ * @format */ -export function helloWorld() { - return true; -} +document.getElementById('root')!.innerText = 'flipper-ui-browser started'; diff --git a/desktop/static/index.web.dev.html b/desktop/static/index.web.dev.html new file mode 100644 index 000000000..6a4c005f0 --- /dev/null +++ b/desktop/static/index.web.dev.html @@ -0,0 +1,117 @@ + + + + + + + + Flipper + + + + +
+
+ Loading... +
+
+ + + + + + + + + diff --git a/desktop/yarn.lock b/desktop/yarn.lock index 0846d70f6..ce094c409 100644 --- a/desktop/yarn.lock +++ b/desktop/yarn.lock @@ -3309,6 +3309,11 @@ abab@^2.0.3, abab@^2.0.5: resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + abort-controller@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" @@ -3606,6 +3611,14 @@ anymatch@^3.0.3: normalize-path "^3.0.0" picomatch "^2.0.4" +anymatch@~3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + app-builder-bin@3.5.13: version "3.5.13" resolved "https://registry.yarnpkg.com/app-builder-bin/-/app-builder-bin-3.5.13.tgz#6dd7f4de34a4e408806f99b8c7d6ef1601305b7e" @@ -4192,6 +4205,11 @@ base@^0.11.1: mixin-deep "^1.2.0" pascalcase "^0.1.1" +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + bl@^1.0.0, bl@^4.0.3, bl@^4.1.0, bl@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/bl/-/bl-5.0.0.tgz#6928804a41e9da9034868e1c50ca88f21f57aea2" @@ -4282,7 +4300,7 @@ braces@^2.3.1: split-string "^3.0.2" to-regex "^3.0.1" -braces@^3.0.1: +braces@^3.0.1, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== @@ -4592,6 +4610,21 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== +chokidar@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" + integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + chownr@^1.1.1: version "1.1.4" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" @@ -6830,6 +6863,11 @@ fsevents@^2.1.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== +fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -6911,7 +6949,7 @@ github-slugger@^1.2.1: dependencies: emoji-regex ">=6.0.0 <=6.1.1" -glob-parent@^5.1.2: +glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== @@ -7285,6 +7323,11 @@ ieee754@^1.1.13, ieee754@^1.2.1: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== +ignore-by-default@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" + integrity sha1-SMptcvbGo68Aqa1K5odr44ieKwk= + ignore-walk@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-4.0.1.tgz#fc840e8346cf88a3a9380c5b17933cd8f4d39fa3" @@ -7481,6 +7524,13 @@ is-bigint@^1.0.1: dependencies: has-bigints "^1.0.1" +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + is-boolean-object@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" @@ -7619,7 +7669,7 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== @@ -9850,6 +9900,29 @@ node-releases@^2.0.1: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA== +nodemon@^2.0.15: + version "2.0.15" + resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.15.tgz#504516ce3b43d9dc9a955ccd9ec57550a31a8d4e" + integrity sha512-gdHMNx47Gw7b3kWxJV64NI+Q5nfl0y5DgDbiVtShiwa7Z0IZ07Ll4RLFo6AjrhzMtoEZn5PDE3/c2AbVsiCkpA== + dependencies: + chokidar "^3.5.2" + debug "^3.2.7" + ignore-by-default "^1.0.1" + minimatch "^3.0.4" + pstree.remy "^1.1.8" + semver "^5.7.1" + supports-color "^5.5.0" + touch "^3.1.0" + undefsafe "^2.0.5" + update-notifier "^5.1.0" + +nopt@~1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" + integrity sha1-bd0hvSoxQXuScn3Vhfim83YI6+4= + dependencies: + abbrev "1" + normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" @@ -9877,7 +9950,7 @@ normalize-path@^2.1.1: dependencies: remove-trailing-separator "^1.0.1" -normalize-path@^3.0.0: +normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== @@ -10401,7 +10474,7 @@ picomatch@^2.0.4, picomatch@^2.0.5: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== -picomatch@^2.2.3: +picomatch@^2.2.1, picomatch@^2.2.3: version "2.3.0" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== @@ -10639,6 +10712,11 @@ psl@^1.1.33: resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== +pstree.remy@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" + integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== + pump@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" @@ -11266,6 +11344,11 @@ react-redux@^7.2.6: prop-types "^15.7.2" react-is "^17.0.2" +react-refresh@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.10.0.tgz#2f536c9660c0b9b1d500684d9e52a65e7404f7e3" + integrity sha512-PgidR3wST3dDYKr6b4pJoqQFpPGNKDSCDx4cZoshjXipw3LzO7mG1My2pwEzz2JVkF+inx3xRpDeQLFQGH/hsQ== + react-refresh@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046" @@ -11417,6 +11500,13 @@ readdir-glob@^1.0.0: dependencies: minimatch "^3.0.4" +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + realpath-native@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-2.0.0.tgz#7377ac429b6e1fd599dc38d08ed942d0d7beb866" @@ -11942,7 +12032,7 @@ semver-diff@^3.1.1: dependencies: semver "^6.3.0" -"semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: +"semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.1: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -12205,7 +12295,7 @@ socket.io-parser@~4.0.4: component-emitter "~1.3.0" debug "~4.3.1" -socket.io@*, socket.io@^4.4.0: +socket.io@*, socket.io@^4.3.1, socket.io@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.4.0.tgz#8140a0db2c22235f88a6dceb867e4d5c9bd70507" integrity sha512-bnpJxswR9ov0Bw6ilhCvO38/1WPtE3eA2dtxi2Iq4/sFebiDJQzgKNYA7AuVVdGW09nrESXd90NbZqtDd9dzRQ== @@ -12542,7 +12632,7 @@ sumchecker@^3.0.1: dependencies: debug "^4.1.0" -supports-color@^5.3.0: +supports-color@^5.3.0, supports-color@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== @@ -12805,6 +12895,13 @@ toidentifier@1.0.0: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== +touch@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b" + integrity sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA== + dependencies: + nopt "~1.0.10" + tough-cookie@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" @@ -13040,6 +13137,11 @@ unbzip2-stream@^1.0.9: buffer "^5.2.1" through "^2.3.8" +undefsafe@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" + integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== + unicode-canonical-property-names-ecmascript@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"