diff --git a/desktop/flipper-common/src/index.tsx b/desktop/flipper-common/src/index.tsx index 019f30144..9759c7aad 100644 --- a/desktop/flipper-common/src/index.tsx +++ b/desktop/flipper-common/src/index.tsx @@ -21,6 +21,7 @@ export {sleep} from './utils/sleep'; export {timeout} from './utils/timeout'; export {isTest} from './utils/isTest'; export {assertNever} from './utils/assertNever'; +export {fsConstants} from './utils/fsConstants'; export { logPlatformSuccessRate, reportPlatformFailures, diff --git a/desktop/flipper-common/src/server-types.tsx b/desktop/flipper-common/src/server-types.tsx index 332cf6bfa..3e0bedce9 100644 --- a/desktop/flipper-common/src/server-types.tsx +++ b/desktop/flipper-common/src/server-types.tsx @@ -140,6 +140,7 @@ export type FlipperServerCommands = { path: string, options?: {recursive?: boolean} & MkdirOptions, ) => Promise; + 'node-api-fs-rm': (path: string, options?: RmOptions) => Promise; 'node-api-fs-copyFile': ( src: string, dest: string, @@ -332,6 +333,10 @@ export interface MkdirOptions { mode?: string | number; } +export interface RmOptions { + maxRetries?: number; +} + export interface DownloadFileStartOptions { method?: 'GET' | 'POST'; timeout?: number; diff --git a/desktop/flipper-common/src/settings.tsx b/desktop/flipper-common/src/settings.tsx index 9b55b4c82..dae556d2e 100644 --- a/desktop/flipper-common/src/settings.tsx +++ b/desktop/flipper-common/src/settings.tsx @@ -69,6 +69,19 @@ export type ProcessConfig = { launcherEnabled: boolean; }; +export type Platform = + | 'aix' + | 'android' + | 'darwin' + | 'freebsd' + | 'haiku' + | 'linux' + | 'openbsd' + | 'sunos' + | 'win32' + | 'cygwin' + | 'netbsd'; + export type EnvironmentInfo = { processId: number; isProduction: boolean; @@ -77,7 +90,7 @@ export type EnvironmentInfo = { appVersion: string; os: { arch: string; - platform: NodeJS.Platform; + platform: Platform; unixname: string; }; versions: { diff --git a/desktop/flipper-common/src/utils/fsConstants.tsx b/desktop/flipper-common/src/utils/fsConstants.tsx new file mode 100644 index 000000000..da0a74b78 --- /dev/null +++ b/desktop/flipper-common/src/utils/fsConstants.tsx @@ -0,0 +1,67 @@ +/** + * 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 + */ + +// https://github.com/nodejs/node/blob/b66a75a3a4361614dde9bc1a52d7e9021b4efc26/typings/internalBinding/constants.d.ts +export const fsConstants = { + UV_FS_SYMLINK_DIR: 1, + UV_FS_SYMLINK_JUNCTION: 2, + O_RDONLY: 0, + O_WRONLY: 1, + O_RDWR: 2, + UV_DIRENT_UNKNOWN: 0, + UV_DIRENT_FILE: 1, + UV_DIRENT_DIR: 2, + UV_DIRENT_LINK: 3, + UV_DIRENT_FIFO: 4, + UV_DIRENT_SOCKET: 5, + UV_DIRENT_CHAR: 6, + UV_DIRENT_BLOCK: 7, + S_IFMT: 61440, + S_IFREG: 32768, + S_IFDIR: 16384, + S_IFCHR: 8192, + S_IFBLK: 24576, + S_IFIFO: 4096, + S_IFLNK: 40960, + S_IFSOCK: 49152, + O_CREAT: 512, + O_EXCL: 2048, + UV_FS_O_FILEMAP: 0, + O_NOCTTY: 131072, + O_TRUNC: 1024, + O_APPEND: 8, + O_DIRECTORY: 1048576, + O_NOFOLLOW: 256, + O_SYNC: 128, + O_DSYNC: 4194304, + O_SYMLINK: 2097152, + O_NONBLOCK: 4, + S_IRWXU: 448, + S_IRUSR: 256, + S_IWUSR: 128, + S_IXUSR: 64, + S_IRWXG: 56, + S_IRGRP: 32, + S_IWGRP: 16, + S_IXGRP: 8, + S_IRWXO: 7, + S_IROTH: 4, + S_IWOTH: 2, + S_IXOTH: 1, + F_OK: 0, + R_OK: 4, + W_OK: 2, + X_OK: 1, + UV_FS_COPYFILE_EXCL: 1, + COPYFILE_EXCL: 1, + UV_FS_COPYFILE_FICLONE: 2, + COPYFILE_FICLONE: 2, + UV_FS_COPYFILE_FICLONE_FORCE: 4, + COPYFILE_FICLONE_FORCE: 4, +}; diff --git a/desktop/flipper-plugin/package.json b/desktop/flipper-plugin/package.json index 8086bab44..c709ad2b3 100644 --- a/desktop/flipper-plugin/package.json +++ b/desktop/flipper-plugin/package.json @@ -13,7 +13,6 @@ "@emotion/css": "^11.5.0", "@emotion/react": "^11.6.0", "@reach/observe-rect": "^1.2.0", - "@types/uuid": "^8.3.1", "eventemitter3": "^4.0.7", "flipper-common": "0.0.0", "immer": "^9.0.6", @@ -27,6 +26,7 @@ }, "devDependencies": { "@types/string-natural-compare": "^3.0.2", + "@types/uuid": "^8.3.3", "jest-mock-console": "^1.2.3", "typescript": "^4.4.4" }, diff --git a/desktop/flipper-plugin/src/__tests__/api.node.tsx b/desktop/flipper-plugin/src/__tests__/api.node.tsx index b2119cd3e..12c875ee1 100644 --- a/desktop/flipper-plugin/src/__tests__/api.node.tsx +++ b/desktop/flipper-plugin/src/__tests__/api.node.tsx @@ -70,6 +70,7 @@ test('Correct top level API exposed', () => { "usePlugin", "useTrackedCallback", "useValue", + "uuid", "withTrackingScope", ] `); diff --git a/desktop/flipper-plugin/src/index.ts b/desktop/flipper-plugin/src/index.ts index 901e7f872..80b7efd84 100644 --- a/desktop/flipper-plugin/src/index.ts +++ b/desktop/flipper-plugin/src/index.ts @@ -133,6 +133,7 @@ export {createTablePlugin} from './utils/createTablePlugin'; export {textContent} from './utils/textContent'; import * as path from './utils/path'; export {path}; +export * from './utils/uuid'; // It's not ideal that this exists in flipper-plugin sources directly, // but is the least pain for plugin authors. diff --git a/desktop/flipper-plugin/src/plugin/FlipperLib.tsx b/desktop/flipper-plugin/src/plugin/FlipperLib.tsx index ee6d619af..c9672f8e8 100644 --- a/desktop/flipper-plugin/src/plugin/FlipperLib.tsx +++ b/desktop/flipper-plugin/src/plugin/FlipperLib.tsx @@ -21,6 +21,9 @@ import { DownloadFileStartOptions, DownloadFileStartResponse, DownloadFileUpdate, + RmOptions, + fsConstants, + EnvironmentInfo, } from 'flipper-common'; export type FileEncoding = 'utf-8' | 'base64'; @@ -57,7 +60,9 @@ export type RemoteServerContext = { path: string, options?: {recursive?: false} & MkdirOptions, ): Promise; + rm(path: string, options?: RmOptions): Promise; copyFile(src: string, dest: string, flags?: number): Promise; + constants: typeof fsConstants; }; downloadFile( url: string, @@ -150,6 +155,10 @@ export interface FlipperLib { paths: { homePath: string; appPath: string; + tempPath: string; + }; + environmentInfo: { + os: EnvironmentInfo['os']; }; remoteServerContext: RemoteServerContext; } diff --git a/desktop/flipper-plugin/src/test-utils/test-utils.tsx b/desktop/flipper-plugin/src/test-utils/test-utils.tsx index 5427f6586..d28477267 100644 --- a/desktop/flipper-plugin/src/test-utils/test-utils.tsx +++ b/desktop/flipper-plugin/src/test-utils/test-utils.tsx @@ -14,7 +14,11 @@ import { act as testingLibAct, } from '@testing-library/react'; import {queries} from '@testing-library/dom'; -import {BundledPluginDetails, InstalledPluginDetails} from 'flipper-common'; +import { + BundledPluginDetails, + fsConstants, + InstalledPluginDetails, +} from 'flipper-common'; import { RealFlipperClient, @@ -388,6 +392,14 @@ export function createMockFlipperLib(options?: StartPluginOptions): FlipperLib { paths: { appPath: process.cwd(), homePath: `/dev/null`, + tempPath: `/dev/null`, + }, + environmentInfo: { + os: { + arch: 'Test', + unixname: 'test', + platform: 'linux', + }, }, remoteServerContext: { childProcess: { @@ -398,7 +410,9 @@ export function createMockFlipperLib(options?: StartPluginOptions): FlipperLib { pathExists: jest.fn(), unlink: jest.fn(), mkdir: jest.fn(), + rm: jest.fn(), copyFile: jest.fn(), + constants: fsConstants, }, downloadFile: jest.fn(), }, diff --git a/desktop/flipper-plugin/src/utils/uuid.tsx b/desktop/flipper-plugin/src/utils/uuid.tsx new file mode 100644 index 000000000..8020deb1c --- /dev/null +++ b/desktop/flipper-plugin/src/utils/uuid.tsx @@ -0,0 +1,11 @@ +/** + * 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 {v4 as uuid} from 'uuid'; +export {uuid}; diff --git a/desktop/flipper-server-core/package.json b/desktop/flipper-server-core/package.json index 4df4cf248..ded959d28 100644 --- a/desktop/flipper-server-core/package.json +++ b/desktop/flipper-server-core/package.json @@ -28,6 +28,7 @@ "open": "^8.3.0", "openssl-wrapper": "^0.3.4", "promisify-child-process": "^4.1.1", + "rimraf": "^3.0.2", "rsocket-core": "^0.0.27", "rsocket-flowable": "^0.0.27", "rsocket-tcp-server": "^0.0.25", @@ -43,6 +44,7 @@ "devDependencies": { "@types/memorystream": "^0.3.0", "@types/node": "^15.12.5", + "@types/rimraf": "^3.0.2", "@types/tmp": "^0.2.2", "memorystream": "^0.3.1", "tmp": "^0.2.1" diff --git a/desktop/flipper-server-core/src/FlipperServerImpl.tsx b/desktop/flipper-server-core/src/FlipperServerImpl.tsx index 88d423792..a5673e9e5 100644 --- a/desktop/flipper-server-core/src/FlipperServerImpl.tsx +++ b/desktop/flipper-server-core/src/FlipperServerImpl.tsx @@ -46,6 +46,8 @@ import { import {commandNodeApiExec} from './commands/NodeApiExec'; import {commandDownloadFileStartFactory} from './commands/DownloadFile'; import {promises} from 'fs'; +// Electron 11 runs on Node 12 which does not support fs.promises.rm +import rm from 'rimraf'; const {access, copyFile, mkdir, unlink} = promises; @@ -230,6 +232,12 @@ export class FlipperServerImpl implements FlipperServer { }, 'node-api-fs-unlink': unlink, 'node-api-fs-mkdir': mkdir, + 'node-api-fs-rm': async (path, {maxRetries} = {}) => + new Promise((resolve, reject) => + rm(path, {disableGlob: true, maxBusyTries: maxRetries}, (err) => + err ? reject(err) : resolve(), + ), + ), 'node-api-fs-copyFile': copyFile, // TODO: Do we need API to cancel an active download? 'download-file-start': commandDownloadFileStartFactory( diff --git a/desktop/flipper-ui-core/src/utils/flipperLibImplementation/index.tsx b/desktop/flipper-ui-core/src/utils/flipperLibImplementation/index.tsx index 0e5596a7f..afa243963 100644 --- a/desktop/flipper-ui-core/src/utils/flipperLibImplementation/index.tsx +++ b/desktop/flipper-ui-core/src/utils/flipperLibImplementation/index.tsx @@ -14,8 +14,10 @@ import { import { BufferEncoding, ExecOptions, + fsConstants, Logger, MkdirOptions, + RmOptions, } from 'flipper-common'; import type {Store} from '../../reducers'; import createPaste from '../../fb-stubs/createPaste'; @@ -71,6 +73,10 @@ export function initializeFlipperLibImplementation( paths: { appPath: renderHost.serverConfig.paths.appPath, homePath: renderHost.serverConfig.paths.homePath, + tempPath: renderHost.serverConfig.paths.tempPath, + }, + environmentInfo: { + os: renderHost.serverConfig.environmentInfo.os, }, remoteServerContext: { childProcess: { @@ -95,6 +101,8 @@ export function initializeFlipperLibImplementation( path, options, )) as RemoteServerContext['fs']['mkdir'], + rm: async (path: string, options?: RmOptions) => + renderHost.flipperServer.exec('node-api-fs-rm', path, options), copyFile: async (src: string, dest: string, flags?: number) => renderHost.flipperServer.exec( 'node-api-fs-copyFile', @@ -102,6 +110,7 @@ export function initializeFlipperLibImplementation( dest, flags, ), + constants: fsConstants, }, downloadFile: downloadFileFactory(renderHost), }, diff --git a/desktop/yarn.lock b/desktop/yarn.lock index ae25f71f9..7478fa0c1 100644 --- a/desktop/yarn.lock +++ b/desktop/yarn.lock @@ -3046,6 +3046,14 @@ resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== +"@types/rimraf@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-3.0.2.tgz#a63d175b331748e5220ad48c901d7bbf1f44eef8" + integrity sha512-F3OznnSLAUxFrCEu/L5PY8+ny8DtcFRjx7fZZ9bycvXRi3KPTRS9HOitGZwvPg0juRhXFWIeKX58cnX5YqLohQ== + dependencies: + "@types/glob" "*" + "@types/node" "*" + "@types/rsocket-core@*", "@types/rsocket-core@^0.0.7": version "0.0.7" resolved "https://registry.yarnpkg.com/@types/rsocket-core/-/rsocket-core-0.0.7.tgz#e9ed6d9ec918ec7a9aab0c48fefbb74b33712235" @@ -3174,6 +3182,11 @@ resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.1.tgz#1a32969cf8f0364b3d8c8af9cc3555b7805df14f" integrity sha512-Y2mHTRAbqfFkpjldbkHGY8JIzRN6XqYRliG8/24FcHm2D2PwW24fl5xMRTVGdrb7iMrwCaIEbLWerGIkXuFWVg== +"@types/uuid@^8.3.3": + version "8.3.3" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.3.tgz#c6a60686d953dbd1b1d45e66f4ecdbd5d471b4d0" + integrity sha512-0LbEEx1zxrYB3pgpd1M5lEhLcXjKJnYghvhTRgaBeUivLHMDM1TzF3IJ6hXU2+8uA4Xz+5BA63mtZo5DjVT8iA== + "@types/verror@^1.10.3": version "1.10.4" resolved "https://registry.yarnpkg.com/@types/verror/-/verror-1.10.4.tgz#805c0612b3a0c124cf99f517364142946b74ba3b" @@ -6795,9 +6808,9 @@ flow-parser@0.*: integrity sha512-IVHejqogTgZL2e206twVsdfX5he6mXS5F0AY315ao+6rEifbElEoVWKLYdEBsVl7QMp4buPbMe5FqXSdYQMUSQ== follow-redirects@^1.14.0: - version "1.14.5" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.5.tgz#f09a5848981d3c772b5392309778523f8d85c381" - integrity sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA== + version "1.14.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.6.tgz#8cfb281bbc035b3c067d6cd975b0f6ade6e855cd" + integrity sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A== for-in@^1.0.2: version "1.0.2" diff --git a/docs/extending/flipper-plugin.mdx b/docs/extending/flipper-plugin.mdx index fcd126bad..bafddb32e 100644 --- a/docs/extending/flipper-plugin.mdx +++ b/docs/extending/flipper-plugin.mdx @@ -1160,6 +1160,12 @@ path.normalize('/foo/bar//baz/asdf/quux/..'); // Returns: '/foo/bar/baz/asdf' ``` +### uuid + +Usage: `uuid()` + +Returns UUID V4. + ## TestUtils The object `TestUtils` as exposed from `flipper-plugin` exposes utilities to write unit tests for Sandy plugins.