Split flipper-plugin package

Summary:
flipper-server-companion depends on flipper-plugin. flipper-plugin includes dependencies that run only in a browser. Splitting flipper-plugin into core and browser packages helps to avoid including browser-only dependencies into flipper-server bundle.
As a result, bundle size could be cut in half. Subsequently, RSS usage drops as there is twice as less code to process for V8.

Note: it currently breaks external flipper-data-source package. It will be restored in subsequent diffs

Reviewed By: lblasa

Differential Revision: D38658285

fbshipit-source-id: 751b11fa9f3a2d938ce166687b8310ba8b059dee
This commit is contained in:
Andrey Goncharov
2022-09-15 10:02:19 -07:00
committed by Facebook GitHub Bot
parent 2090120cda
commit 97b8b8a1c4
86 changed files with 813 additions and 645 deletions

View File

@@ -12,7 +12,7 @@
"dependencies": {
"eventemitter3": "^4.0.7",
"flipper-common": "0.0.0",
"flipper-plugin": "0.0.0",
"flipper-plugin-core": "0.0.0",
"immer": "^9.0.12",
"js-base64": "^3.7.2",
"p-map": "^4.0.0",

View File

@@ -30,7 +30,7 @@ import {
_SandyPluginInstance,
getFlipperLib,
_SandyPluginDefinition,
} from 'flipper-plugin';
} from 'flipper-plugin-core';
import {createServerAddOnControls} from './utils/createServerAddOnControls';
import isProduction from './utils/isProduction';
@@ -135,17 +135,23 @@ export default abstract class AbstractClient extends EventEmitter {
initialState?: Record<string, any>,
) {
try {
this.sandyPluginStates.set(
plugin.id,
new _SandyPluginInstance(
this.serverAddOnControls,
getFlipperLib(),
plugin,
this,
getPluginKey(this.id, {serial: this.query.device_id}, plugin.id),
initialState,
),
const pluginInstance = new _SandyPluginInstance(
this.serverAddOnControls,
getFlipperLib(),
plugin,
this,
getPluginKey(this.id, {serial: this.query.device_id}, plugin.id),
initialState,
);
pluginInstance.events.on('error', (message) => {
const error: ClientErrorType = {
message,
name: 'Plugin Error',
stacktrace: '',
};
this.emit('error', error);
});
this.sandyPluginStates.set(plugin.id, pluginInstance);
} catch (e) {
console.error(`Failed to start plugin '${plugin.id}': `, e);
}

View File

@@ -7,7 +7,7 @@
* @format
*/
import {FlipperLib, Notification} from 'flipper-plugin';
import {FlipperLib, Notification} from 'flipper-plugin-core';
import {FlipperServer, FlipperServerConfig} from 'flipper-common';
type NotificationEvents = 'show' | 'click' | 'close' | 'reply' | 'action';

View File

@@ -15,7 +15,7 @@ import {
getLatestCompatibleVersionOfEachPlugin,
} from '../plugins';
import {BundledPluginDetails, InstalledPluginDetails} from 'flipper-common';
import {_SandyPluginDefinition} from 'flipper-plugin';
import {_SandyPluginDefinition} from 'flipper-plugin-core';
import {getRenderHostInstance} from '../RenderHost';
let loadDynamicPluginsMock: jest.Mock;

View File

@@ -8,7 +8,7 @@
*/
import BaseDevice from './BaseDevice';
import type {DeviceOS, DeviceType} from 'flipper-plugin';
import type {DeviceOS, DeviceType} from 'flipper-plugin-core';
export default class ArchivedDevice extends BaseDevice {
isArchived = true;

View File

@@ -16,7 +16,7 @@ import {
createState,
getFlipperLib,
CrashLogListener,
} from 'flipper-plugin';
} from 'flipper-plugin-core';
import {
DeviceLogEntry,
DeviceOS,
@@ -49,7 +49,11 @@ export default class BaseDevice implements Device {
hasDevicePlugins = false; // true if there are device plugins for this device (not necessarily enabled)
private readonly serverAddOnControls: ServerAddOnControls;
constructor(flipperServer: FlipperServer, description: DeviceDescription) {
constructor(
flipperServer: FlipperServer,
description: DeviceDescription,
private pluginErrorHandler?: (msg: string) => void,
) {
this.flipperServer = flipperServer;
this.description = description;
this.serverAddOnControls = createServerAddOnControls(this.flipperServer);
@@ -341,18 +345,19 @@ export default class BaseDevice implements Device {
this.hasDevicePlugins = true;
if (plugin instanceof _SandyPluginDefinition) {
try {
this.sandyPluginStates.set(
plugin.id,
new _SandyDevicePluginInstance(
this.serverAddOnControls,
getFlipperLib(),
plugin,
this,
// break circular dep, one of those days again...
getPluginKey(undefined, {serial: this.serial}, plugin.id),
initialState,
),
const pluginInstance = new _SandyDevicePluginInstance(
this.serverAddOnControls,
getFlipperLib(),
plugin,
this,
// break circular dep, one of those days again...
getPluginKey(undefined, {serial: this.serial}, plugin.id),
initialState,
);
if (this.pluginErrorHandler) {
pluginInstance.events.on('error', this.pluginErrorHandler);
}
this.sandyPluginStates.set(plugin.id, pluginInstance);
} catch (e) {
console.error(`Failed to start device plugin '${plugin.id}': `, e);
}

View File

@@ -7,7 +7,7 @@
* @format
*/
import type {DeviceOS, DeviceType} from 'flipper-plugin';
import type {DeviceOS, DeviceType} from 'flipper-plugin-core';
import {DeviceSpec} from 'flipper-common';
import BaseDevice from './BaseDevice';
import {getRenderHostInstance} from '../RenderHost';

View File

@@ -15,7 +15,7 @@ import {
TestUtils,
_SandyPluginDefinition,
_setFlipperLibImplementation,
} from 'flipper-plugin';
} from 'flipper-plugin-core';
import {default as ArchivedDevice} from '../ArchivedDevice';
import {TestDevice} from '../TestDevice';

View File

@@ -8,7 +8,7 @@
*/
import {assertNever, DownloadFileUpdate} from 'flipper-common';
import {FlipperLib, DownloadFileResponse} from 'flipper-plugin';
import {FlipperLib, DownloadFileResponse} from 'flipper-plugin-core';
import {RenderHost} from '../RenderHost';
export const downloadFileFactory =

View File

@@ -7,7 +7,7 @@
* @format
*/
import {RemoteServerContext, FlipperLib} from 'flipper-plugin';
import {RemoteServerContext, FlipperLib} from 'flipper-plugin-core';
import {
BufferEncoding,
ExecOptions,

View File

@@ -18,7 +18,7 @@ import {
ConcretePluginDetails,
} from 'flipper-common';
import {reportUsage} from 'flipper-common';
import {_SandyPluginDefinition} from 'flipper-plugin';
import {_SandyPluginDefinition} from 'flipper-plugin-core';
import isPluginCompatible from './utils/isPluginCompatible';
import isPluginVersionMoreRecent from './utils/isPluginVersionMoreRecent';
import {getRenderHostInstance} from './RenderHost';

View File

@@ -11,7 +11,7 @@
"path": "../flipper-common"
},
{
"path": "../flipper-plugin"
"path": "../flipper-plugin-core"
},
{
"path": "../test-utils"

View File

@@ -0,0 +1 @@
# flipper-plugin-core

View File

@@ -0,0 +1,40 @@
{
"name": "flipper-plugin-core",
"version": "0.0.0",
"description": "Flipper Desktop plugin SDK and components",
"repository": "facebook/flipper",
"main": "lib/index.js",
"flipperBundlerEntry": "src",
"types": "lib/index.d.ts",
"license": "MIT",
"bugs": "https://github.com/facebook/flipper/issues",
"dependencies": {
"eventemitter3": "^4.0.7",
"flipper-common": "0.0.0",
"immer": "^9.0.12",
"js-base64": "^3.7.2",
"lodash": "^4.17.21",
"string-natural-compare": "^3.0.0"
},
"devDependencies": {
"@types/react": "17.0.39",
"@types/string-natural-compare": "^3.0.2",
"jest-mock-console": "^1.2.3"
},
"peerDependencies": {
"@testing-library/dom": "^7.26.3"
},
"scripts": {
"reset": "rimraf lib *.tsbuildinfo",
"build": "tsc -b",
"prepack": "yarn reset && yarn build"
},
"files": [
"lib/**/*"
],
"homepage": "https://github.com/facebook/flipper",
"keywords": [
"Flipper"
],
"author": "Facebook, Inc"
}

View File

@@ -0,0 +1,111 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
// Dummy exports to support running plugin code in a headless context.
// We do not want to bundle real code that is going to be used in a browser context to decrease the bundle size.
// Yet some parts of the browser-only code is being evaluated at plugin import, not when it is being rendered.
// Expand the list of stubs as needed when we onboard more and more headless plugins
export const theme = {};
export const styled = () => () => ({});
export {produce, Draft} from 'immer';
import * as TestUtilites from './test-utils/test-utils';
export const TestUtils = TestUtilites;
export {StartPluginOptions as _StartPluginOptions} from './test-utils/test-utils';
import './plugin/PluginBase';
export {BasePluginInstance as _BasePluginInstance} from './plugin/PluginBase';
export {
SandyPluginInstance as _SandyPluginInstance,
PluginClient,
PluginFactory as _PluginFactory,
RealFlipperClient as _RealFlipperClient,
} from './plugin/Plugin';
export {
Device,
DeviceLogListener,
DevicePluginClient,
CrashLogListener,
SandyDevicePluginInstance as _SandyDevicePluginInstance,
DevicePluginFactory as _DevicePluginFactory,
} from './plugin/DevicePlugin';
export {
SandyPluginDefinition as _SandyPluginDefinition,
FlipperPluginInstance,
FlipperPluginModule as _FlipperPluginModule,
FlipperDevicePluginModule as _FlipperDevicePluginModule,
} from './plugin/SandyPluginDefinition';
export {
DataSource,
DataSourceView as _DataSourceView,
DataSourceOptionKey as _DataSourceOptionKey,
DataSourceOptions as _DataSourceOptions,
} from './data-source/DataSource';
export {createDataSource} from './state/createDataSource';
export {
createState,
Atom,
isAtom,
ReadOnlyAtom as _ReadOnlyAtom,
AtomValue as _AtomValue,
} from './state/atom';
export {
setBatchedUpdateImplementation as _setBatchedUpdateImplementation,
batch,
} from './state/batch';
export {
FlipperLib,
getFlipperLib,
setFlipperLibImplementation as _setFlipperLibImplementation,
tryGetFlipperLibImplementation as _tryGetFlipperLibImplementation,
FileDescriptor,
FileEncoding,
RemoteServerContext,
DownloadFileResponse,
} from './plugin/FlipperLib';
export {
MenuEntry,
NormalizedMenuEntry,
buildInMenuEntries as _buildInMenuEntries,
DefaultKeyboardAction,
} from './plugin/MenuEntry';
export {Notification} from './plugin/Notification';
export {CreatePasteArgs, CreatePasteResult} from './plugin/Paste';
export {Idler} from './utils/Idler';
export {
makeShallowSerializable as _makeShallowSerializable,
deserializeShallowObject as _deserializeShallowObject,
} from './utils/shallowSerialization';
import * as path from './utils/path';
export {path};
export {safeStringify} from './utils/safeStringify';
export {stubLogger as _stubLogger} from './utils/Logger';
export {
sleep,
timeout,
createControlledPromise,
uuid,
DeviceOS,
DeviceType,
DeviceLogEntry,
DeviceLogLevel,
Logger,
CrashLog,
ServerAddOn,
ServerAddOnPluginConnection,
FlipperServerForServerAddOn,
} from 'flipper-common';

View File

@@ -7,12 +7,12 @@
* @format
*/
import type {ReactElement} from 'react';
import {Logger} from '../utils/Logger';
import {Device} from './DevicePlugin';
import {NormalizedMenuEntry} from './MenuEntry';
import {RealFlipperClient} from './Plugin';
import {Notification} from './Notification';
import {DetailSidebarProps} from '../ui/DetailSidebar';
import {
ExecOptions,
ExecOut,
@@ -109,9 +109,11 @@ export interface FlipperLib {
writeTextToClipboard(text: string): void;
openLink(url: string): void;
showNotification(pluginKey: string, notification: Notification): void;
DetailsSidebarImplementation?(
props: DetailSidebarProps,
): React.ReactElement | null;
DetailsSidebarImplementation?(props: {
children: any;
width?: number;
minWidth?: number;
}): ReactElement | null;
/**
* @returns
* Imported file data.

View File

@@ -7,10 +7,11 @@
* @format
*/
import type {ReactNode} from 'react';
export type Notification = {
id: string;
title: string;
message: string | React.ReactNode;
message: string | ReactNode;
severity: 'warning' | 'error';
timestamp?: number;
category?: string;

View File

@@ -18,6 +18,7 @@ import {
EventsContract,
MethodsContract,
} from 'flipper-common';
import type {FC} from 'react';
type PreventIntersectionWith<Contract extends Record<string, any>> = {
[Key in keyof Contract]?: never;
@@ -140,7 +141,7 @@ export type PluginFactory<
client: PluginClient<Events, Methods, ServerAddOnEvents, ServerAddOnMethods>,
) => object;
export type FlipperPluginComponent = React.FC<{}>;
export type FlipperPluginComponent = FC<{}>;
export class SandyPluginInstance extends BasePluginInstance {
static is(thing: any): thing is SandyPluginInstance {

View File

@@ -7,7 +7,6 @@
* @format
*/
import {message} from 'antd';
import EventEmitter from 'eventemitter3';
import {SandyPluginDefinition} from './SandyPluginDefinition';
import {MenuEntry, NormalizedMenuEntry, normalizeMenuEntry} from './MenuEntry';
@@ -312,7 +311,7 @@ export abstract class BasePluginInstance {
// msg is already specific
// eslint-disable-next-line
console.error(msg, e);
message.error(msg);
this.events.emit('error', msg);
}
}
this.initialStates = undefined;
@@ -325,7 +324,7 @@ export abstract class BasePluginInstance {
// msg is already specific
// eslint-disable-next-line
console.error(msg, e);
message.error(msg);
this.events.emit('error', msg);
}
}

View File

@@ -0,0 +1,141 @@
/**
* Copyright (c) Meta Platforms, Inc. and 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 {produce, Draft, enableMapSet} from 'immer';
import {
getCurrentPluginInstance,
Persistable,
registerStorageAtom,
} from '../plugin/PluginBase';
import {
deserializeShallowObject,
makeShallowSerializable,
} from '../utils/shallowSerialization';
enableMapSet();
export interface ReadOnlyAtom<T> {
get(): T;
subscribe(listener: (value: T, prevValue: T) => void): () => void;
unsubscribe(listener: (value: T, prevValue: T) => void): void;
}
export interface Atom<T> extends ReadOnlyAtom<T> {
set(newValue: T): void;
update(recipe: (draft: Draft<T>) => void): void;
update<X extends T>(recipe: (draft: X) => void): void;
}
export class AtomValue<T> implements Atom<T>, Persistable {
value: T;
listeners: ((value: T, prevValue: T) => void)[] = [];
constructor(initialValue: T) {
this.value = initialValue;
}
get() {
return this.value;
}
set(nextValue: T) {
if (nextValue !== this.value) {
const prevValue = this.value;
this.value = nextValue;
this.notifyChanged(prevValue);
}
}
deserialize(value: T) {
this.set(deserializeShallowObject(value));
}
serialize() {
return makeShallowSerializable(this.get());
}
update(recipe: (draft: Draft<T>) => void) {
this.set(produce(this.value, recipe));
}
notifyChanged(prevValue: T) {
// TODO: add scheduling
this.listeners.slice().forEach((l) => l(this.value, prevValue));
}
subscribe(listener: (value: T, prevValue: T) => void) {
this.listeners.push(listener);
return () => this.unsubscribe(listener);
}
unsubscribe(listener: (value: T, prevValue: T) => void) {
const idx = this.listeners.indexOf(listener);
if (idx !== -1) {
this.listeners.splice(idx, 1);
}
}
}
type StateOptions = {
/**
* Should this state persist when exporting a plugin?
* If set, the atom will be saved / loaded under the key provided
*/
persist?: string;
/**
* Store this state in local storage, instead of as part of the plugin import / export.
* State stored in local storage is shared between the same plugin
* across multiple clients/ devices, but not actively synced.
*/
persistToLocalStorage?: boolean;
};
export function createState<T>(
initialValue: T,
options?: StateOptions,
): Atom<T>;
export function createState<T>(): Atom<T | undefined>;
export function createState(
initialValue: any = undefined,
options: StateOptions = {},
): Atom<any> {
const atom = new AtomValue(initialValue);
if (options?.persistToLocalStorage) {
syncAtomWithLocalStorage(options, atom);
} else {
registerStorageAtom(options.persist, atom);
}
return atom;
}
function syncAtomWithLocalStorage(options: StateOptions, atom: AtomValue<any>) {
if (!options?.persist) {
throw new Error(
"The 'persist' option should be set when 'persistToLocalStorage' is set",
);
}
const pluginInstance = getCurrentPluginInstance();
if (!pluginInstance) {
throw new Error(
"The 'persistToLocalStorage' option cannot be used outside a plugin definition",
);
}
const storageKey = `flipper:${pluginInstance.definition.id}:atom:${options.persist}`;
const storedValue = window.localStorage.getItem(storageKey);
if (storedValue != null) {
atom.deserialize(JSON.parse(storedValue));
}
atom.subscribe(() => {
window.localStorage.setItem(storageKey, JSON.stringify(atom.serialize()));
});
}
export function isAtom(value: any): value is Atom<any> {
return value instanceof AtomValue;
}

View File

@@ -0,0 +1,29 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
export let batch: (callback: (...args: any[]) => void) => void = (callback) =>
callback();
export const setBatchedUpdateImplementation = (
impl: (callback: (...args: any[]) => void) => void,
) => {
batch = impl;
};
export function batched<T extends Function>(fn: T): T;
export function batched(fn: any) {
return function (this: any) {
let res: any;
batch(() => {
// eslint-disable-next-line
res = fn.apply(this, arguments);
});
return res;
};
}

View File

@@ -12,7 +12,7 @@ import {
createDataSource as baseCreateDataSource,
DataSourceOptions as BaseDataSourceOptions,
DataSourceOptionKey as BaseDataSourceOptionKey,
} from '../data-source/index';
} from '../data-source/DataSource';
import {registerStorageAtom} from '../plugin/PluginBase';
type DataSourceOptions = BaseDataSourceOptions & {

View File

@@ -0,0 +1,209 @@
/**
* Copyright (c) Meta Platforms, Inc. and 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 {
BundledPluginDetails,
fsConstants,
InstalledPluginDetails,
} from 'flipper-common';
import {FlipperServer, FlipperServerCommands} from 'flipper-common';
import {Device} from '../plugin/DevicePlugin';
import {FlipperLib} from '../plugin/FlipperLib';
import {PluginFactory} from '../plugin/Plugin';
import {
FlipperDevicePluginModule,
FlipperPluginModule,
SandyPluginDefinition,
} from '../plugin/SandyPluginDefinition';
import {stubLogger} from '../utils/Logger';
declare const process: any;
export interface StartPluginOptions {
initialState?: Record<string, any>;
isArchived?: boolean;
isBackgroundPlugin?: boolean;
startUnactivated?: boolean;
/** Provide a set of unsupported methods to simulate older clients that don't support certain methods yet */
unsupportedMethods?: string[];
/**
* Provide a set of GKs that are enabled in this test.
*/
GKs?: string[];
testDevice?: Device;
}
export function createStubFunction(): jest.Mock<any, any> {
// we shouldn't be usign jest.fn() outside a unit test, as it would not resolve / cause jest to be bundled up!
if (typeof jest !== 'undefined') {
return jest.fn();
}
return (() => {
console.warn('Using a stub function outside a test environment!');
}) as any;
}
export function createMockFlipperLib(options?: StartPluginOptions): FlipperLib {
return {
isFB: false,
logger: stubLogger,
enableMenuEntries: createStubFunction(),
createPaste: createStubFunction(),
GK(gk: string) {
return options?.GKs?.includes(gk) || false;
},
selectPlugin: createStubFunction(),
writeTextToClipboard: createStubFunction(),
openLink: createStubFunction(),
showNotification: createStubFunction(),
exportFile: createStubFunction(),
importFile: createStubFunction(),
paths: {
appPath: process.cwd(),
homePath: `/dev/null`,
staticPath: process.cwd(),
tempPath: `/dev/null`,
},
environmentInfo: {
os: {
arch: 'Test',
unixname: 'test',
platform: 'linux',
},
},
intern: {
graphGet: createStubFunction(),
graphPost: createStubFunction(),
},
remoteServerContext: {
childProcess: {
exec: createStubFunction(),
},
fs: {
access: createStubFunction(),
pathExists: createStubFunction(),
unlink: createStubFunction(),
mkdir: createStubFunction(),
rm: createStubFunction(),
copyFile: createStubFunction(),
constants: fsConstants,
stat: createStubFunction(),
readlink: createStubFunction(),
readFile: createStubFunction(),
readFileBinary: createStubFunction(),
writeFile: createStubFunction(),
writeFileBinary: createStubFunction(),
},
downloadFile: createStubFunction(),
},
};
}
export function createMockPluginDetails(
details?: Partial<InstalledPluginDetails>,
): InstalledPluginDetails {
return {
id: 'TestPlugin',
dir: '',
name: 'TestPlugin',
specVersion: 0,
entry: '',
isBundled: false,
isActivatable: true,
main: '',
source: '',
title: 'Testing Plugin',
version: '',
...details,
};
}
export function createTestPlugin<T extends PluginFactory<any, any, any, any>>(
implementation: Pick<FlipperPluginModule<T>, 'plugin'> &
Partial<FlipperPluginModule<T>>,
details?: Partial<InstalledPluginDetails>,
) {
return new SandyPluginDefinition(
createMockPluginDetails({
pluginType: 'client',
...details,
}),
{
Component() {
return null;
},
...implementation,
},
);
}
export function createTestDevicePlugin(
implementation: Pick<FlipperDevicePluginModule, 'devicePlugin'> &
Partial<FlipperDevicePluginModule>,
details?: Partial<InstalledPluginDetails>,
) {
return new SandyPluginDefinition(
createMockPluginDetails({
pluginType: 'device',
...details,
}),
{
supportsDevice() {
return true;
},
Component() {
return null;
},
...implementation,
},
);
}
export function createMockBundledPluginDetails(
details?: Partial<BundledPluginDetails>,
): BundledPluginDetails {
return {
id: 'TestBundledPlugin',
name: 'TestBundledPlugin',
specVersion: 0,
pluginType: 'client',
isBundled: true,
isActivatable: true,
main: '',
source: '',
title: 'Testing Bundled Plugin',
version: '',
...details,
};
}
export function createFlipperServerMock(
overrides?: Partial<FlipperServerCommands>,
): FlipperServer {
return {
async connect() {},
on: createStubFunction(),
off: createStubFunction(),
exec: jest
.fn()
.mockImplementation(
async (cmd: keyof FlipperServerCommands, ...args: any[]) => {
if (overrides?.[cmd]) {
return (overrides[cmd] as any)(...args);
}
console.warn(
`Empty server response stubbed for command '${cmd}', set 'getRenderHostInstance().flipperServer.exec' in your test to override the behavior.`,
);
return undefined;
},
),
close: createStubFunction(),
};
}

View File

@@ -0,0 +1,14 @@
{
"extends": "../tsconfig.base.json",
"compilerOptions": {
"outDir": "lib",
"rootDir": "src",
"lib": ["dom", "ES2019"],
"types": ["jest", "../types/jest-extensions", "react/next", "react-dom/next"]
},
"references": [
{
"path": "../flipper-common"
}
]
}

View File

@@ -18,6 +18,7 @@
"@types/react-dom": "^17.0.13",
"eventemitter3": "^4.0.7",
"flipper-common": "0.0.0",
"flipper-plugin-core": "0.0.0",
"immer": "^9.0.12",
"js-base64": "^3.7.2",
"lodash": "^4.17.21",

View File

@@ -8,9 +8,10 @@
*/
import * as React from 'react';
import {DevicePluginClient, Device} from '../plugin/DevicePlugin';
import {DevicePluginClient, Device} from 'flipper-plugin-core';
import {usePlugin} from '../plugin/PluginContext';
import {createState, useValue} from '../state/atom';
import {createState} from 'flipper-plugin-core';
import {useValue} from '../state/atom';
export function supportsDevice(_device: Device) {
return true;

View File

@@ -8,9 +8,10 @@
*/
import * as React from 'react';
import {PluginClient} from '../plugin/Plugin';
import {PluginClient} from 'flipper-plugin-core';
import {usePlugin} from '../plugin/PluginContext';
import {createState, useValue} from '../state/atom';
import {useValue} from '../state/atom';
import {createState} from 'flipper-plugin-core';
type Events = {
inc: {

View File

@@ -9,7 +9,7 @@
import * as TestUtils from '../test-utils/test-utils';
import * as testPlugin from './DeviceTestPlugin';
import {createState} from '../state/atom';
import {createState} from 'flipper-plugin-core';
const testLogMessage = {
date: new Date(),

View File

@@ -9,12 +9,12 @@
import * as TestUtils from '../test-utils/test-utils';
import * as testPlugin from './TestPlugin';
import {createState} from '../state/atom';
import {PluginClient} from '../plugin/Plugin';
import {DevicePluginClient} from '../plugin/DevicePlugin';
import {createState} from 'flipper-plugin-core';
import {PluginClient} from 'flipper-plugin-core';
import {DevicePluginClient} from 'flipper-plugin-core';
import mockConsole from 'jest-mock-console';
import {sleep} from 'flipper-common';
import {createDataSource} from '../state/createDataSource';
import {createDataSource} from 'flipper-plugin-core';
test('it can start a plugin and lifecycle events', () => {
const {instance, ...p} = TestUtils.startPlugin(testPlugin);
@@ -357,9 +357,6 @@ test('plugins can handle import errors', async () => {
"An error occurred when importing data for plugin 'TestPlugin': 'Error: Oops",
[Error: Oops],
],
Array [
"Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot",
],
]
`);
} finally {

View File

@@ -7,7 +7,8 @@
* @format
*/
import {DataSourceView} from './DataSource';
// eslint-disable-next-line node/no-extraneous-import
import type {_DataSourceView} from 'flipper-plugin-core';
import React, {memo, useCallback, useEffect, useState} from 'react';
import {RedrawContext} from './DataSourceRendererVirtual';
@@ -16,7 +17,7 @@ type DataSourceProps<T extends object, C> = {
/**
* The data view to render
*/
dataView: DataSourceView<T, T[keyof T]>;
dataView: _DataSourceView<T, T[keyof T]>;
/**
* additional context that will be passed verbatim to the itemRenderer, so that it can be easily memoized
*/
@@ -35,7 +36,7 @@ type DataSourceProps<T extends object, C> = {
onUpdateAutoScroll?(autoScroll: boolean): void;
emptyRenderer?:
| null
| ((dataView: DataSourceView<T, T[keyof T]>) => React.ReactElement);
| ((dataView: _DataSourceView<T, T[keyof T]>) => React.ReactElement);
};
/**

View File

@@ -18,7 +18,8 @@ import React, {
useContext,
createContext,
} from 'react';
import {DataSourceView} from './DataSource';
// eslint-disable-next-line node/no-extraneous-import
import type {_DataSourceView} from 'flipper-plugin-core';
import {useVirtual} from 'react-virtual';
import observeRect from '@reach/observe-rect';
@@ -39,7 +40,7 @@ type DataSourceProps<T extends object, C> = {
/**
* The data source to render
*/
dataView: DataSourceView<T, T[keyof T]>;
dataView: _DataSourceView<T, T[keyof T]>;
/**
* Automatically scroll if the user is near the end?
*/
@@ -68,7 +69,7 @@ type DataSourceProps<T extends object, C> = {
onUpdateAutoScroll?(autoScroll: boolean): void;
emptyRenderer?:
| null
| ((dataView: DataSourceView<T, T[keyof T]>) => React.ReactElement);
| ((dataView: _DataSourceView<T, T[keyof T]>) => React.ReactElement);
};
/**

View File

@@ -52,12 +52,12 @@ The significant difference to many other solutions is that DataSource doesn't pr
Instead, it keeps internally a mutable dataset (the records stored themselves are still immutable but can be replaced) to which new entries are added.
However, instead of propagating the dataset to the rendering layer, events are emitted instead.
### DataSourceView
### _DataSourceView
Conceptually, `DataSourceView` is a materialized view of a `DataSource`.
Conceptually, `_DataSourceView` is a materialized view of a `DataSource`.
For visualizations, typically the following transformations need to be applied: filter/search, sorting and windowing.
Where many libraries applies these transformations as part of the _rendering_, DataSourceView applies these operations directly when updates to the dataset are received.
Where many libraries applies these transformations as part of the _rendering_, _DataSourceView applies these operations directly when updates to the dataset are received.
As a result the transformations need to be applied only to the newly arriving data.
For example, if a new record arrives for a sorted dataset, we will apply a binary inseration sort for the new entry, avoiding the need for a full re-sort of the dataset during Rendering.
@@ -66,11 +66,11 @@ The events will describe how the current view should be updated to reflect the d
### DataSourceRendererVirtual
`DataSourceRendererVirtual` is one of the possible visualizations of a DataSourceView.
It takes care of subscribing to the events emitted by the `DataSourceView`, and applies them when they are relevant (e.g. within the visible window).
Beyond that, it manages virtualizations (using the `react-virtual` library), so that for example scroll interactions are used to move the window of the`DataSourceView`.
`DataSourceRendererVirtual` is one of the possible visualizations of a _DataSourceView.
It takes care of subscribing to the events emitted by the `_DataSourceView`, and applies them when they are relevant (e.g. within the visible window).
Beyond that, it manages virtualizations (using the `react-virtual` library), so that for example scroll interactions are used to move the window of the`_DataSourceView`.
Typically this component is used as underlying abstraction for a Table representation.
Typically this component is used as underlying abstraction for a Table representation.
### DataSourceRendererStatic
@@ -163,7 +163,7 @@ Project setup:
Features:
* [ ] **Support multiple DataSourceView's per DataSource**: Currently there is a one view per source limitation because we didn't need more yet.
* [ ] **Support multiple _DataSourceView's per DataSource**: Currently there is a one view per source limitation because we didn't need more yet.
* [ ] **Break up operations that process the full data set in smaller tasks**: There are several operations that process the full data set, for example changing the sort / filter criteria. Currently this is done synchronously (and we debounce changing the filter), in the future we will split up the filtering in smaller taks to make it efficient. But we don't have a way to efficiently break down sorting into smaller tasks as using insertion sorting is 20x slower than the native sorting mechanism if the full data set needs to be processed.
* [ ] **Add built-in support for downsampling data**
* [ ] **Leverage React concurrent mode**: Currently there is custom scheduler logic to handle high- and low- (outside window) priority updates. In principle this could probably be achieved through React concurrent mode as well, but ANT.design (which is used in Flipper) doesn't support it yet.

View File

@@ -9,11 +9,11 @@
export {
DataSource,
DataSourceView,
_DataSourceView,
createDataSource,
DataSourceOptions,
DataSourceOptionKey,
} from './DataSource';
_DataSourceOptions,
_DataSourceOptionKey, // eslint-disable-next-line node/no-extraneous-import
} from 'flipper-plugin-core';
export {
DataSourceRendererVirtual,
DataSourceVirtualizer,

View File

@@ -7,52 +7,22 @@
* @format
*/
export {produce, Draft} from 'immer';
export * from 'flipper-plugin-core';
import styledImport from '@emotion/styled';
export const styled = styledImport;
import './plugin/PluginBase';
import './state/batch';
export {useValue} from './state/atom';
import * as TestUtilites from './test-utils/test-utils';
export {
SandyPluginInstance as _SandyPluginInstance,
PluginClient,
} from './plugin/Plugin';
export {
Device,
DeviceLogListener,
DevicePluginClient,
CrashLogListener,
SandyDevicePluginInstance as _SandyDevicePluginInstance,
} from './plugin/DevicePlugin';
export {
SandyPluginDefinition as _SandyPluginDefinition,
FlipperPluginInstance,
} from './plugin/SandyPluginDefinition';
export {SandyPluginRenderer as _SandyPluginRenderer} from './plugin/PluginRenderer';
export {
SandyPluginContext as _SandyPluginContext,
usePlugin,
} from './plugin/PluginContext';
export {createState, useValue, Atom, isAtom} from './state/atom';
export {batch} from './state/batch';
export {
FlipperLib,
getFlipperLib,
setFlipperLibImplementation as _setFlipperLibImplementation,
FileDescriptor,
FileEncoding,
RemoteServerContext,
DownloadFileResponse,
} from './plugin/FlipperLib';
export {
MenuEntry,
NormalizedMenuEntry,
buildInMenuEntries as _buildInMenuEntries,
DefaultKeyboardAction,
} from './plugin/MenuEntry';
export {Notification} from './plugin/Notification';
export {CreatePasteArgs, CreatePasteResult} from './plugin/Paste';
export {theme} from './ui/theme';
export {Layout} from './ui/Layout';
@@ -83,12 +53,6 @@ export {DataFormatter} from './ui/DataFormatter';
export {useLogger, _LoggerContext} from './utils/useLogger';
export {Idler} from './utils/Idler';
// Import from the index file directly, to make sure package.json's main field is skipped.
export {DataSource} from './data-source/index';
export {createDataSource} from './state/createDataSource';
export {DataTable, DataTableColumn} from './ui/data-table/DataTable';
export {DataTableManager} from './ui/data-table/DataTableManager';
export {DataList} from './ui/DataList';
@@ -127,36 +91,13 @@ export {
ElementID,
} from './ui/elements-inspector/ElementsInspector';
export {useMemoize} from './utils/useMemoize';
export {
makeShallowSerializable as _makeShallowSerializable,
deserializeShallowObject as _deserializeShallowObject,
} from './utils/shallowSerialization';
export {createTablePlugin} from './utils/createTablePlugin';
export {textContent} from './utils/textContent';
import * as path from './utils/path';
export {path};
export {safeStringify} from './utils/safeStringify';
// It's not ideal that this exists in flipper-plugin sources directly,
// but is the least pain for plugin authors.
// Probably we should make sure that testing-library doesn't end up in our final Flipper bundle (which packages flipper-plugin)
// T69106962
export const TestUtils = TestUtilites;
export {
sleep,
timeout,
createControlledPromise,
uuid,
DeviceOS,
DeviceType,
DeviceLogEntry,
DeviceLogLevel,
Logger,
CrashLog,
ServerAddOn,
ServerAddOnPluginConnection,
FlipperServerForServerAddOn,
} from 'flipper-common';

View File

@@ -8,16 +8,20 @@
*/
import {createContext, useContext} from 'react';
import {SandyPluginInstance, PluginFactory} from './Plugin';
import {SandyDevicePluginInstance, DevicePluginFactory} from './DevicePlugin';
import {
_SandyDevicePluginInstance,
_DevicePluginFactory,
_SandyPluginInstance,
_PluginFactory,
} from 'flipper-plugin-core';
export const SandyPluginContext = createContext<
SandyPluginInstance | SandyDevicePluginInstance | undefined
_SandyPluginInstance | _SandyDevicePluginInstance | undefined
>(undefined);
export function usePluginInstance():
| SandyPluginInstance
| SandyDevicePluginInstance {
| _SandyPluginInstance
| _SandyDevicePluginInstance {
const pluginInstance = useContext(SandyPluginContext);
if (!pluginInstance) {
throw new Error('Sandy Plugin context not available');
@@ -26,14 +30,14 @@ export function usePluginInstance():
}
export function usePluginInstanceMaybe():
| SandyPluginInstance
| SandyDevicePluginInstance
| _SandyPluginInstance
| _SandyDevicePluginInstance
| undefined {
return useContext(SandyPluginContext);
}
export function usePlugin<
Factory extends PluginFactory<any, any, any, any> | DevicePluginFactory,
Factory extends _PluginFactory<any, any, any, any> | _DevicePluginFactory,
>(plugin: Factory): ReturnType<Factory> {
const pluginInstance = usePluginInstance();
// In principle we don't *need* the plugin, but having it passed it makes sure the

View File

@@ -9,20 +9,22 @@
import React, {memo, useEffect, createElement} from 'react';
import {SandyPluginContext} from './PluginContext';
import {SandyPluginInstance} from './Plugin';
import {SandyDevicePluginInstance} from './DevicePlugin';
import {BasePluginInstance} from './PluginBase';
import {
_SandyPluginInstance,
_SandyDevicePluginInstance,
_BasePluginInstance,
} from 'flipper-plugin-core';
import {TrackingScope} from '../ui/Tracked';
type Props = {
plugin: SandyPluginInstance | SandyDevicePluginInstance;
plugin: _SandyPluginInstance | _SandyDevicePluginInstance;
};
/**
* Component to render a Sandy plugin container
*/
export const SandyPluginRenderer = memo(({plugin}: Props) => {
if (!plugin || !(plugin instanceof BasePluginInstance)) {
if (!plugin || !(plugin instanceof _BasePluginInstance)) {
throw new Error('Expected plugin, got ' + plugin);
}
useEffect(() => {

View File

@@ -1,24 +0,0 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
// Exports for server add-ons
import * as path from './utils/path';
export {path};
export {safeStringify} from './utils/safeStringify';
export {
sleep,
timeout,
createControlledPromise,
uuid,
ServerAddOn,
ServerAddOnPluginConnection,
FlipperServerForServerAddOn,
} from 'flipper-common';

View File

@@ -7,7 +7,7 @@
* @format
*/
import {createState} from '../atom';
import {createState} from 'flipper-plugin-core';
import * as TestUtils from '../../test-utils/test-utils';
beforeEach(() => {

View File

@@ -7,143 +7,16 @@
* @format
*/
import {produce, Draft, enableMapSet} from 'immer';
import {_AtomValue, _ReadOnlyAtom} from 'flipper-plugin-core';
import {useState, useEffect} from 'react';
import {
getCurrentPluginInstance,
Persistable,
registerStorageAtom,
} from '../plugin/PluginBase';
import {
deserializeShallowObject,
makeShallowSerializable,
} from '../utils/shallowSerialization';
enableMapSet();
export interface ReadOnlyAtom<T> {
get(): T;
subscribe(listener: (value: T, prevValue: T) => void): () => void;
unsubscribe(listener: (value: T, prevValue: T) => void): void;
}
export interface Atom<T> extends ReadOnlyAtom<T> {
set(newValue: T): void;
update(recipe: (draft: Draft<T>) => void): void;
update<X extends T>(recipe: (draft: X) => void): void;
}
class AtomValue<T> implements Atom<T>, Persistable {
value: T;
listeners: ((value: T, prevValue: T) => void)[] = [];
constructor(initialValue: T) {
this.value = initialValue;
}
get() {
return this.value;
}
set(nextValue: T) {
if (nextValue !== this.value) {
const prevValue = this.value;
this.value = nextValue;
this.notifyChanged(prevValue);
}
}
deserialize(value: T) {
this.set(deserializeShallowObject(value));
}
serialize() {
return makeShallowSerializable(this.get());
}
update(recipe: (draft: Draft<T>) => void) {
this.set(produce(this.value, recipe));
}
notifyChanged(prevValue: T) {
// TODO: add scheduling
this.listeners.slice().forEach((l) => l(this.value, prevValue));
}
subscribe(listener: (value: T, prevValue: T) => void) {
this.listeners.push(listener);
return () => this.unsubscribe(listener);
}
unsubscribe(listener: (value: T, prevValue: T) => void) {
const idx = this.listeners.indexOf(listener);
if (idx !== -1) {
this.listeners.splice(idx, 1);
}
}
}
type StateOptions = {
/**
* Should this state persist when exporting a plugin?
* If set, the atom will be saved / loaded under the key provided
*/
persist?: string;
/**
* Store this state in local storage, instead of as part of the plugin import / export.
* State stored in local storage is shared between the same plugin
* across multiple clients/ devices, but not actively synced.
*/
persistToLocalStorage?: boolean;
};
export function createState<T>(
initialValue: T,
options?: StateOptions,
): Atom<T>;
export function createState<T>(): Atom<T | undefined>;
export function createState(
initialValue: any = undefined,
options: StateOptions = {},
): Atom<any> {
const atom = new AtomValue(initialValue);
if (options?.persistToLocalStorage) {
syncAtomWithLocalStorage(options, atom);
} else {
registerStorageAtom(options.persist, atom);
}
return atom;
}
function syncAtomWithLocalStorage(options: StateOptions, atom: AtomValue<any>) {
if (!options?.persist) {
throw new Error(
"The 'persist' option should be set when 'persistToLocalStorage' is set",
);
}
const pluginInstance = getCurrentPluginInstance();
if (!pluginInstance) {
throw new Error(
"The 'persistToLocalStorage' option cannot be used outside a plugin definition",
);
}
const storageKey = `flipper:${pluginInstance.definition.id}:atom:${options.persist}`;
const storedValue = window.localStorage.getItem(storageKey);
if (storedValue != null) {
atom.deserialize(JSON.parse(storedValue));
}
atom.subscribe(() => {
window.localStorage.setItem(storageKey, JSON.stringify(atom.serialize()));
});
}
export function useValue<T>(atom: ReadOnlyAtom<T>): T;
export function useValue<T>(atom: _ReadOnlyAtom<T>): T;
export function useValue<T>(
atom: ReadOnlyAtom<T> | undefined,
atom: _ReadOnlyAtom<T> | undefined,
defaultValue: T,
): T;
export function useValue<T>(
atom: ReadOnlyAtom<T> | undefined,
atom: _ReadOnlyAtom<T> | undefined,
defaultValue?: T,
): T {
const [localValue, setLocalValue] = useState<T>(
@@ -156,14 +29,10 @@ export function useValue<T>(
// atom might have changed between mounting and effect setup
// in that case, this will cause a re-render, otherwise not
setLocalValue(atom.get());
(atom as AtomValue<T>).subscribe(setLocalValue);
(atom as _AtomValue<T>).subscribe(setLocalValue);
return () => {
(atom as AtomValue<T>).unsubscribe(setLocalValue);
(atom as _AtomValue<T>).unsubscribe(setLocalValue);
};
}, [atom]);
return localValue;
}
export function isAtom(value: any): value is Atom<any> {
return value instanceof AtomValue;
}

View File

@@ -8,17 +8,6 @@
*/
import {unstable_batchedUpdates} from 'react-dom';
import {_setBatchedUpdateImplementation} from 'flipper-plugin-core';
export const batch = unstable_batchedUpdates;
export function batched<T extends Function>(fn: T): T;
export function batched(fn: any) {
return function (this: any) {
let res: any;
batch(() => {
// eslint-disable-next-line
res = fn.apply(this, arguments);
});
return res;
};
}
_setBatchedUpdateImplementation(unstable_batchedUpdates);

View File

@@ -10,71 +10,45 @@
import * as React from 'react';
import type {RenderResult} from '@testing-library/react';
import {queries} from '@testing-library/dom';
import {
BundledPluginDetails,
fsConstants,
InstalledPluginDetails,
ServerAddOnControls,
} from 'flipper-common';
import {ServerAddOnControls} from 'flipper-common';
import {
RealFlipperClient,
SandyPluginInstance,
_RealFlipperClient,
_SandyPluginInstance,
PluginClient,
PluginFactory,
} from '../plugin/Plugin';
import {
SandyPluginDefinition,
FlipperPluginModule,
FlipperDevicePluginModule,
} from '../plugin/SandyPluginDefinition';
import {SandyPluginRenderer} from '../plugin/PluginRenderer';
import {
SandyDevicePluginInstance,
_PluginFactory,
_SandyPluginDefinition,
_FlipperPluginModule,
_FlipperDevicePluginModule,
_SandyDevicePluginInstance,
Device,
DeviceLogListener,
CrashLogListener,
} from '../plugin/DevicePlugin';
import {BasePluginInstance} from '../plugin/PluginBase';
import {FlipperLib} from '../plugin/FlipperLib';
import {stubLogger} from '../utils/Logger';
import {Idler} from '../utils/Idler';
import {createState} from '../state/atom';
import {
DeviceLogEntry,
FlipperServer,
FlipperServerCommands,
} from 'flipper-common';
_BasePluginInstance,
FlipperLib,
_stubLogger,
Idler,
createState,
TestUtils,
_StartPluginOptions,
} from 'flipper-plugin-core';
import {SandyPluginRenderer} from '../plugin/PluginRenderer';
import {DeviceLogEntry} from 'flipper-common';
declare const process: any;
declare const electronRequire: any;
type Renderer = RenderResult<typeof queries>;
interface StartPluginOptions {
initialState?: Record<string, any>;
isArchived?: boolean;
isBackgroundPlugin?: boolean;
startUnactivated?: boolean;
/** Provide a set of unsupported methods to simulate older clients that don't support certain methods yet */
unsupportedMethods?: string[];
/**
* Provide a set of GKs that are enabled in this test.
*/
GKs?: string[];
testDevice?: Device;
}
type ExtractClientType<Module extends FlipperPluginModule<any>> = Parameters<
type ExtractClientType<Module extends _FlipperPluginModule<any>> = Parameters<
Module['plugin']
>[0];
type ExtractMethodsType<Module extends FlipperPluginModule<any>> =
type ExtractMethodsType<Module extends _FlipperPluginModule<any>> =
ExtractClientType<Module> extends PluginClient<any, infer Methods>
? Methods
: never;
type ExtractEventsType<Module extends FlipperPluginModule<any>> =
type ExtractEventsType<Module extends _FlipperPluginModule<any>> =
ExtractClientType<Module> extends PluginClient<infer Events, any>
? Events
: never;
@@ -125,7 +99,7 @@ interface BasePluginResult {
serverAddOnControls: ServerAddOnControls;
}
interface StartPluginResult<Module extends FlipperPluginModule<any>>
interface StartPluginResult<Module extends _FlipperPluginModule<any>>
extends BasePluginResult {
/**
* the instantiated plugin for this test
@@ -173,7 +147,7 @@ interface StartPluginResult<Module extends FlipperPluginModule<any>>
): void;
}
interface StartDevicePluginResult<Module extends FlipperDevicePluginModule>
interface StartDevicePluginResult<Module extends _FlipperDevicePluginModule>
extends BasePluginResult {
/**
* the instantiated plugin for this test
@@ -189,24 +163,14 @@ interface StartDevicePluginResult<Module extends FlipperDevicePluginModule>
sendLogEntry(logEntry: DeviceLogEntry): void;
}
export function createStubFunction(): jest.Mock<any, any> {
// we shouldn't be usign jest.fn() outside a unit test, as it would not resolve / cause jest to be bundled up!
if (typeof jest !== 'undefined') {
return jest.fn();
}
return (() => {
console.warn('Using a stub function outside a test environment!');
}) as any;
}
export function startPlugin<Module extends FlipperPluginModule<any>>(
export function startPlugin<Module extends _FlipperPluginModule<any>>(
module: Module,
options?: StartPluginOptions,
options?: _StartPluginOptions,
): StartPluginResult<Module> {
const {act} = electronRequire('@testing-library/react');
const definition = new SandyPluginDefinition(
createMockPluginDetails(),
const definition = new _SandyPluginDefinition(
TestUtils.createMockPluginDetails(),
module,
);
if (definition.isDevicePlugin) {
@@ -215,12 +179,12 @@ export function startPlugin<Module extends FlipperPluginModule<any>>(
);
}
const sendStub = createStubFunction();
const flipperUtils = createMockFlipperLib(options);
const sendStub = TestUtils.createStubFunction();
const flipperUtils = TestUtils.createMockFlipperLib(options);
const testDevice = createMockDevice(options);
const appName = 'TestApplication';
const deviceName = 'TestDevice';
const fakeFlipperClient: RealFlipperClient = {
const fakeFlipperClient: _RealFlipperClient = {
id: `${appName}#${testDevice.os}#${deviceName}#${testDevice.serial}`,
plugins: new Set([definition.id]),
query: {
@@ -263,7 +227,7 @@ export function startPlugin<Module extends FlipperPluginModule<any>>(
const serverAddOnControls = createServerAddOnControlsMock();
const pluginInstance = new SandyPluginInstance(
const pluginInstance = new _SandyPluginInstance(
serverAddOnControls,
flipperUtils,
definition,
@@ -305,9 +269,9 @@ export function startPlugin<Module extends FlipperPluginModule<any>>(
return res;
}
export function renderPlugin<Module extends FlipperPluginModule<any>>(
export function renderPlugin<Module extends _FlipperPluginModule<any>>(
module: Module,
options?: StartPluginOptions,
options?: _StartPluginOptions,
): StartPluginResult<Module> & {
renderer: Renderer;
act: (cb: () => void) => void;
@@ -315,7 +279,7 @@ export function renderPlugin<Module extends FlipperPluginModule<any>>(
// prevent bundling in UI bundle
const {render, act} = electronRequire('@testing-library/react');
const res = startPlugin(module, options);
const pluginInstance: SandyPluginInstance = (res as any)._backingInstance;
const pluginInstance: _SandyPluginInstance = (res as any)._backingInstance;
const renderer = render(<SandyPluginRenderer plugin={pluginInstance} />);
@@ -330,14 +294,14 @@ export function renderPlugin<Module extends FlipperPluginModule<any>>(
};
}
export function startDevicePlugin<Module extends FlipperDevicePluginModule>(
export function startDevicePlugin<Module extends _FlipperDevicePluginModule>(
module: Module,
options?: StartPluginOptions,
options?: _StartPluginOptions,
): StartDevicePluginResult<Module> {
const {act} = electronRequire('@testing-library/react');
const definition = new SandyPluginDefinition(
createMockPluginDetails({pluginType: 'device'}),
const definition = new _SandyPluginDefinition(
TestUtils.createMockPluginDetails({pluginType: 'device'}),
module,
);
if (!definition.isDevicePlugin) {
@@ -346,10 +310,10 @@ export function startDevicePlugin<Module extends FlipperDevicePluginModule>(
);
}
const flipperLib = createMockFlipperLib(options);
const flipperLib = TestUtils.createMockFlipperLib(options);
const testDevice = createMockDevice(options);
const serverAddOnControls = createServerAddOnControlsMock();
const pluginInstance = new SandyDevicePluginInstance(
const pluginInstance = new _SandyDevicePluginInstance(
serverAddOnControls,
flipperLib,
definition,
@@ -376,9 +340,9 @@ export function startDevicePlugin<Module extends FlipperDevicePluginModule>(
return res;
}
export function renderDevicePlugin<Module extends FlipperDevicePluginModule>(
export function renderDevicePlugin<Module extends _FlipperDevicePluginModule>(
module: Module,
options?: StartPluginOptions,
options?: _StartPluginOptions,
): StartDevicePluginResult<Module> & {
renderer: Renderer;
act: (cb: () => void) => void;
@@ -387,7 +351,7 @@ export function renderDevicePlugin<Module extends FlipperDevicePluginModule>(
const res = startDevicePlugin(module, options);
// @ts-ignore hidden api
const pluginInstance: SandyDevicePluginInstance = (res as any)
const pluginInstance: _SandyDevicePluginInstance = (res as any)
._backingInstance;
const renderer = render(<SandyPluginRenderer plugin={pluginInstance} />);
@@ -403,64 +367,8 @@ export function renderDevicePlugin<Module extends FlipperDevicePluginModule>(
};
}
export function createMockFlipperLib(options?: StartPluginOptions): FlipperLib {
return {
isFB: false,
logger: stubLogger,
enableMenuEntries: createStubFunction(),
createPaste: createStubFunction(),
GK(gk: string) {
return options?.GKs?.includes(gk) || false;
},
selectPlugin: createStubFunction(),
writeTextToClipboard: createStubFunction(),
openLink: createStubFunction(),
showNotification: createStubFunction(),
exportFile: createStubFunction(),
importFile: createStubFunction(),
paths: {
appPath: process.cwd(),
homePath: `/dev/null`,
staticPath: process.cwd(),
tempPath: `/dev/null`,
},
environmentInfo: {
os: {
arch: 'Test',
unixname: 'test',
platform: 'linux',
},
},
intern: {
graphGet: createStubFunction(),
graphPost: createStubFunction(),
},
remoteServerContext: {
childProcess: {
exec: createStubFunction(),
},
fs: {
access: createStubFunction(),
pathExists: createStubFunction(),
unlink: createStubFunction(),
mkdir: createStubFunction(),
rm: createStubFunction(),
copyFile: createStubFunction(),
constants: fsConstants,
stat: createStubFunction(),
readlink: createStubFunction(),
readFile: createStubFunction(),
readFileBinary: createStubFunction(),
writeFile: createStubFunction(),
writeFileBinary: createStubFunction(),
},
downloadFile: createStubFunction(),
},
};
}
function createBasePluginResult(
pluginInstance: BasePluginInstance,
pluginInstance: _BasePluginInstance,
serverAddOnControls: ServerAddOnControls,
): BasePluginResult {
return {
@@ -491,85 +399,7 @@ function createBasePluginResult(
};
}
export function createMockPluginDetails(
details?: Partial<InstalledPluginDetails>,
): InstalledPluginDetails {
return {
id: 'TestPlugin',
dir: '',
name: 'TestPlugin',
specVersion: 0,
entry: '',
isBundled: false,
isActivatable: true,
main: '',
source: '',
title: 'Testing Plugin',
version: '',
...details,
};
}
export function createTestPlugin<T extends PluginFactory<any, any, any, any>>(
implementation: Pick<FlipperPluginModule<T>, 'plugin'> &
Partial<FlipperPluginModule<T>>,
details?: Partial<InstalledPluginDetails>,
) {
return new SandyPluginDefinition(
createMockPluginDetails({
pluginType: 'client',
...details,
}),
{
Component() {
return null;
},
...implementation,
},
);
}
export function createTestDevicePlugin(
implementation: Pick<FlipperDevicePluginModule, 'devicePlugin'> &
Partial<FlipperDevicePluginModule>,
details?: Partial<InstalledPluginDetails>,
) {
return new SandyPluginDefinition(
createMockPluginDetails({
pluginType: 'device',
...details,
}),
{
supportsDevice() {
return true;
},
Component() {
return null;
},
...implementation,
},
);
}
export function createMockBundledPluginDetails(
details?: Partial<BundledPluginDetails>,
): BundledPluginDetails {
return {
id: 'TestBundledPlugin',
name: 'TestBundledPlugin',
specVersion: 0,
pluginType: 'client',
isBundled: true,
isActivatable: true,
main: '',
source: '',
title: 'Testing Bundled Plugin',
version: '',
...details,
};
}
function createMockDevice(options?: StartPluginOptions): Device & {
function createMockDevice(options?: _StartPluginOptions): Device & {
addLogEntry(entry: DeviceLogEntry): void;
} {
const logListeners: (undefined | DeviceLogListener)[] = [];
@@ -608,18 +438,18 @@ function createMockDevice(options?: StartPluginOptions): Device & {
addLogEntry(entry: DeviceLogEntry) {
logListeners.forEach((f) => f?.(entry));
},
executeShell: createStubFunction(),
clearLogs: createStubFunction(),
forwardPort: createStubFunction(),
executeShell: TestUtils.createStubFunction(),
clearLogs: TestUtils.createStubFunction(),
forwardPort: TestUtils.createStubFunction(),
get isConnected() {
return this.connected.get();
},
installApp(_: string) {
return Promise.resolve();
},
navigateToLocation: createStubFunction(),
screenshot: createStubFunction(),
sendMetroCommand: createStubFunction(),
navigateToLocation: TestUtils.createStubFunction(),
screenshot: TestUtils.createStubFunction(),
sendMetroCommand: TestUtils.createStubFunction(),
};
}
@@ -638,38 +468,22 @@ function createStubIdler(): Idler {
};
}
export function createFlipperServerMock(
overrides?: Partial<FlipperServerCommands>,
): FlipperServer {
function createServerAddOnControlsMock(): ServerAddOnControls {
return {
async connect() {},
on: createStubFunction(),
off: createStubFunction(),
exec: jest
.fn()
.mockImplementation(
async (cmd: keyof FlipperServerCommands, ...args: any[]) => {
if (overrides?.[cmd]) {
return (overrides[cmd] as any)(...args);
}
console.warn(
`Empty server response stubbed for command '${cmd}', set 'getRenderHostInstance().flipperServer.exec' in your test to override the behavior.`,
);
return undefined;
},
),
close: createStubFunction(),
start: TestUtils.createStubFunction(),
stop: TestUtils.createStubFunction(),
sendMessage: TestUtils.createStubFunction(),
receiveMessage: TestUtils.createStubFunction(),
receiveAnyMessage: TestUtils.createStubFunction(),
unsubscribePlugin: TestUtils.createStubFunction(),
unsubscribe: TestUtils.createStubFunction(),
};
}
function createServerAddOnControlsMock(): ServerAddOnControls {
return {
start: createStubFunction(),
stop: createStubFunction(),
sendMessage: createStubFunction(),
receiveMessage: createStubFunction(),
receiveAnyMessage: createStubFunction(),
unsubscribePlugin: createStubFunction(),
unsubscribe: createStubFunction(),
};
}
export const createMockFlipperLib = TestUtils.createMockFlipperLib;
export const createMockPluginDetails = TestUtils.createMockPluginDetails;
export const createTestPlugin = TestUtils.createTestPlugin;
export const createTestDevicePlugin = TestUtils.createTestDevicePlugin;
export const createMockBundledPluginDetails =
TestUtils.createMockBundledPluginDetails;
export const createFlipperServerMock = TestUtils.createFlipperServerMock;

View File

@@ -15,8 +15,8 @@ import {
import {Button, Typography} from 'antd';
import {isPlainObject, pad} from 'lodash';
import React, {createElement, Fragment, isValidElement, useState} from 'react';
import {tryGetFlipperLibImplementation} from '../plugin/FlipperLib';
import {safeStringify} from '../utils/safeStringify';
import {_tryGetFlipperLibImplementation} from 'flipper-plugin-core';
import {safeStringify} from 'flipper-plugin-core';
import {urlRegex} from '../utils/urlRegex';
import {useTableRedraw} from '../data-source/index';
import {theme} from './theme';
@@ -196,7 +196,7 @@ export function TruncateHelper({
<Button
icon={<CopyOutlined />}
onClick={() => {
tryGetFlipperLibImplementation()?.writeTextToClipboard(value);
_tryGetFlipperLibImplementation()?.writeTextToClipboard(value);
}}
size="small"
type="text"

View File

@@ -22,7 +22,7 @@ import {theme} from './theme';
import styled from '@emotion/styled';
import {DataTableManager} from './data-table/DataTableManager';
import {useAssertStableRef} from '../utils/useAssertStableRef';
import {DataSource} from '../data-source';
import {DataSource} from 'flipper-plugin-core';
import {useMakeStableCallback} from '../utils/useMakeStableCallback';
const {Text} = Typography;
@@ -95,7 +95,9 @@ export const DataList: (<T extends object>(
}: DataListProps<T>) {
// if a tableManagerRef is provided, we piggy back on that same ref
// eslint-disable-next-line
const tableManagerRef = tableProps.tableManagerRef ?? createRef<undefined | DataTableManager<T>>();
const tableManagerRef =
tableProps.tableManagerRef ??
createRef<undefined | DataTableManager<T>>();
useAssertStableRef(onRenderItem, 'onRenderItem');
useAssertStableRef(enableArrow, 'enableArrow');

View File

@@ -8,7 +8,7 @@
*/
import React from 'react';
import {tryGetFlipperLibImplementation} from '../plugin/FlipperLib';
import {_tryGetFlipperLibImplementation} from 'flipper-plugin-core';
import {Layout} from './Layout';
export type DetailSidebarProps = {
@@ -19,7 +19,7 @@ export type DetailSidebarProps = {
/* eslint-disable react-hooks/rules-of-hooks */
export function DetailSidebar(props: DetailSidebarProps) {
const lib = tryGetFlipperLibImplementation();
const lib = _tryGetFlipperLibImplementation();
if (lib?.DetailsSidebarImplementation) {
return <lib.DetailsSidebarImplementation {...props} />;
}

View File

@@ -8,7 +8,8 @@
*/
import {Alert, ButtonProps, Input, Modal, Radio, Space, Typography} from 'antd';
import {createState, useValue} from '../state/atom';
import {createState} from 'flipper-plugin-core';
import {useValue} from '../state/atom';
import React from 'react';
import {renderReactRoot} from '../utils/renderReactRoot';
import {Layout} from './Layout';

View File

@@ -24,7 +24,7 @@ import {
FileEncoding,
FlipperLib,
getFlipperLib,
} from '../plugin/FlipperLib';
} from 'flipper-plugin-core';
import {fromUint8Array} from 'js-base64';
import {assertNever} from 'flipper-common';

View File

@@ -29,8 +29,9 @@ import {
} from '@ant-design/icons';
import {Button} from 'antd';
import {usePluginInstance} from '../plugin/PluginContext';
import {Atom, createState, useValue} from '../state/atom';
import {Atom, createState} from 'flipper-plugin-core';
import {useAssertStableRef} from '../utils/useAssertStableRef';
import {useValue} from '../state/atom';
type MasterDetailProps<T> = {
/**

View File

@@ -19,12 +19,13 @@ import styled from '@emotion/styled';
import {keyframes} from '@emotion/css';
import reactElementToJSXString from 'react-element-to-jsx-string';
import {SandyPluginContext} from '../plugin/PluginContext';
import {createState, useValue} from '../state/atom';
import {SandyDevicePluginInstance} from '../plugin/DevicePlugin';
import {createState} from 'flipper-plugin-core';
import {useValue} from '../state/atom';
import {_SandyDevicePluginInstance} from 'flipper-plugin-core';
import {Layout} from './Layout';
import {BulbTwoTone} from '@ant-design/icons';
import type {TooltipPlacement} from 'antd/lib/tooltip';
import {SandyPluginInstance} from '../plugin/Plugin';
import {_SandyPluginInstance} from 'flipper-plugin-core';
import {theme} from './theme';
import {Tracked} from './Tracked';
import {sha256} from '../utils/sha256';
@@ -37,7 +38,7 @@ const storageKey = `FLIPPER_NUX_STATE`;
export async function getNuxKey(
elem: React.ReactNode,
currentPlugin?: SandyPluginInstance | SandyDevicePluginInstance,
currentPlugin?: _SandyPluginInstance | _SandyDevicePluginInstance,
): Promise<string> {
const hash = await sha256(reactElementToJSXString(elem));
return `${currentPlugin?.definition.id ?? 'flipper'}:${hash}`;
@@ -59,14 +60,14 @@ export function createNuxManager() {
return {
async markRead(
elem: React.ReactNode,
currentPlugin?: SandyPluginInstance | SandyDevicePluginInstance,
currentPlugin?: _SandyPluginInstance | _SandyDevicePluginInstance,
): Promise<void> {
readMap[await getNuxKey(elem, currentPlugin)] = true;
save();
},
async isRead(
elem: React.ReactNode,
currentPlugin?: SandyPluginInstance | SandyDevicePluginInstance,
currentPlugin?: _SandyPluginInstance | _SandyDevicePluginInstance,
): Promise<boolean> {
return !!readMap[await getNuxKey(elem, currentPlugin)];
},

View File

@@ -26,8 +26,8 @@ import {getSortedKeys} from './utils';
import React from 'react';
import {useHighlighter, HighlightManager} from '../Highlight';
import {Dropdown, Menu, Tooltip} from 'antd';
import {tryGetFlipperLibImplementation} from '../../plugin/FlipperLib';
import {safeStringify} from '../../utils/safeStringify';
import {_tryGetFlipperLibImplementation} from 'flipper-plugin-core';
import {safeStringify} from 'flipper-plugin-core';
import {useInUnitTest} from '../../utils/useInUnitTest';
import {theme} from '../theme';
@@ -615,7 +615,7 @@ export const DataInspectorNode: React.FC<DataInspectorProps> = memo(
}
function getContextMenu() {
const lib = tryGetFlipperLibImplementation();
const lib = _tryGetFlipperLibImplementation();
const extraItems = additionalContextMenuItems
? [
additionalContextMenuItems(parentPath, value, name),

View File

@@ -26,8 +26,6 @@ import {Percentage} from '../../utils/widthUtils';
import {
DataSourceRendererVirtual,
DataSourceRendererStatic,
DataSource,
DataSourceView,
DataSourceVirtualizer,
} from '../../data-source/index';
import {
@@ -53,7 +51,11 @@ import {Formatter} from '../DataFormatter';
import {usePluginInstanceMaybe} from '../../plugin/PluginContext';
import {debounce} from 'lodash';
import {useInUnitTest} from '../../utils/useInUnitTest';
import {createDataSource} from '../../state/createDataSource';
import {
createDataSource,
DataSource,
_DataSourceView,
} from 'flipper-plugin-core';
import {HighlightProvider} from '../Highlight';
import {useLatestRef} from '../../utils/useLatestRef';
@@ -77,7 +79,7 @@ type DataTableBaseProps<T = any> = {
onContextMenu?: (selection: undefined | T) => React.ReactElement;
onRenderEmpty?:
| null
| ((dataView?: DataSourceView<T, T[keyof T]>) => React.ReactElement);
| ((dataView?: _DataSourceView<T, T[keyof T]>) => React.ReactElement);
};
export type ItemRenderer<T> = (
@@ -771,7 +773,7 @@ function syncRecordsToDataSource<T>(
}
function createDefaultEmptyRenderer<T>(dataTableManager?: DataTableManager<T>) {
return (dataView?: DataSourceView<T, T[keyof T]>) => (
return (dataView?: _DataSourceView<T, T[keyof T]>) => (
<EmptyTable dataView={dataView} dataManager={dataTableManager} />
);
}
@@ -780,7 +782,7 @@ function EmptyTable<T>({
dataView,
dataManager,
}: {
dataView?: DataSourceView<T, T[keyof T]>;
dataView?: _DataSourceView<T, T[keyof T]>;
dataManager?: DataTableManager<T>;
}) {
const resetFilters = useCallback(() => {

View File

@@ -10,13 +10,10 @@
import type {DataTableColumn} from './DataTable';
import {Percentage} from '../../utils/widthUtils';
import {MutableRefObject, Reducer} from 'react';
import {
DataSource,
DataSourceView,
DataSourceVirtualizer,
} from '../../data-source/index';
import {DataSourceVirtualizer} from '../../data-source/index';
import produce, {castDraft, immerable, original} from 'immer';
import {theme} from '../theme';
import {DataSource, _DataSourceView} from 'flipper-plugin-core';
export type OnColumnResize = (id: string, size: number | Percentage) => void;
export type Sorting<T = any> = {
@@ -121,7 +118,7 @@ type DataManagerActions<T> =
type DataManagerConfig<T> = {
dataSource: DataSource<T, T[keyof T]>;
dataView: DataSourceView<T, T[keyof T]>;
dataView: _DataSourceView<T, T[keyof T]>;
defaultColumns: DataTableColumn<T>[];
scope: string;
onSelect: undefined | ((item: T | undefined, items: T[]) => void);
@@ -299,7 +296,7 @@ export const dataTableManagerReducer = produce<
}
case 'setColumnFilterFromSelection': {
const items = getSelectedItems(
config.dataView as DataSourceView<any, any>,
config.dataView as _DataSourceView<any, any>,
draft.selection,
);
items.forEach((item, index) => {
@@ -376,7 +373,7 @@ export type DataTableManager<T> = {
toggleColumnVisibility(column: keyof T): void;
sortColumn(column: keyof T, direction?: SortDirection): void;
setSearchValue(value: string, addToHistory?: boolean): void;
dataView: DataSourceView<T, T[keyof T]>;
dataView: _DataSourceView<T, T[keyof T]>;
toggleSearchValue(): void;
toggleHighlightSearch(): void;
setSearchHighlightColor(color: string): void;
@@ -386,7 +383,7 @@ export type DataTableManager<T> = {
};
export function createDataTableManager<T>(
dataView: DataSourceView<T, T[keyof T]>,
dataView: _DataSourceView<T, T[keyof T]>,
dispatch: DataTableDispatch<T>,
stateRef: MutableRefObject<DataManagerState<T>>,
): DataTableManager<T> {
@@ -535,14 +532,14 @@ function addColumnFilter<T>(
}
export function getSelectedItem<T>(
dataView: DataSourceView<T, T[keyof T]>,
dataView: _DataSourceView<T, T[keyof T]>,
selection: Selection,
): T | undefined {
return selection.current < 0 ? undefined : dataView.get(selection.current);
}
export function getSelectedItems<T>(
dataView: DataSourceView<T, T[keyof T]>,
dataView: _DataSourceView<T, T[keyof T]>,
selection: Selection,
): T[] {
return [...selection.items]

View File

@@ -19,10 +19,12 @@ import {
Selection,
} from './DataTableManager';
import React from 'react';
import {tryGetFlipperLibImplementation} from '../../plugin/FlipperLib';
import {
_tryGetFlipperLibImplementation,
_DataSourceView,
} from 'flipper-plugin-core';
import {DataTableColumn} from './DataTable';
import {toFirstUpper} from '../../utils/toFirstUpper';
import {DataSourceView} from '../../data-source/index';
import {renderColumnValue} from './TableRow';
import {textContent} from '../../utils/textContent';
import {theme} from '../theme';
@@ -31,7 +33,7 @@ const {Item, SubMenu} = Menu;
const {Option} = Select;
export function tableContextMenuFactory<T>(
dataView: DataSourceView<T, T[keyof T]>,
dataView: _DataSourceView<T, T[keyof T]>,
dispatch: DataTableDispatch<T>,
selection: Selection,
highlightSearchSetting: SearchHighlightSetting,
@@ -45,7 +47,7 @@ export function tableContextMenuFactory<T>(
onContextMenu?: (selection: undefined | T) => React.ReactElement,
sideBySideOption?: React.ReactElement,
) {
const lib = tryGetFlipperLibImplementation();
const lib = _tryGetFlipperLibImplementation();
if (!lib) {
return (
<Menu>

View File

@@ -10,7 +10,7 @@
import React, {createRef} from 'react';
import {DataTable, DataTableColumn} from '../DataTable';
import {render, act} from '@testing-library/react';
import {createDataSource} from '../../../state/createDataSource';
import {createDataSource} from 'flipper-plugin-core';
import {computeDataTableFilter, DataTableManager} from '../DataTableManager';
import {Button} from 'antd';
import {sleep} from 'flipper-common';

View File

@@ -16,8 +16,8 @@ import {theme} from '../theme';
import {Layout} from '../Layout';
import {
getFlipperLib,
tryGetFlipperLibImplementation,
} from '../../plugin/FlipperLib';
_tryGetFlipperLibImplementation,
} from 'flipper-plugin-core';
import {DownOutlined, RightOutlined} from '@ant-design/icons';
const {Text} = Typography;
@@ -533,7 +533,7 @@ export class Elements extends PureComponent<ElementsProps, ElementsState> {
};
isDarwin =
tryGetFlipperLibImplementation()?.environmentInfo.os.platform === 'darwin';
_tryGetFlipperLibImplementation()?.environmentInfo.os.platform === 'darwin';
onKeyDown = (e: KeyboardEvent<any>) => {
const {selected} = this.props;

View File

@@ -8,14 +8,14 @@
*/
import {notification, Typography} from 'antd';
import {DataSource} from '../data-source/index';
import {DataSource} from 'flipper-plugin-core';
import React from 'react';
import {PluginClient} from '../plugin/Plugin';
import {PluginClient} from 'flipper-plugin-core';
import {usePlugin} from '../plugin/PluginContext';
import {createState} from '../state/atom';
import {createState} from 'flipper-plugin-core';
import {DataTableColumn} from '../ui/data-table/DataTable';
import {MasterDetail} from '../ui/MasterDetail';
import {createDataSource} from '../state/createDataSource';
import {createDataSource} from 'flipper-plugin-core';
type PluginResult<Raw, Row> = {
plugin(client: PluginClient<Record<string, Raw | {}>>): {

View File

@@ -7,7 +7,8 @@
* @format
*/
import {createState, useValue} from '../state/atom';
import {createState} from 'flipper-plugin-core';
import {useValue} from '../state/atom';
import React, {ReactPortal} from 'react';
import {createPortal, unmountComponentAtNode} from 'react-dom';

View File

@@ -9,9 +9,9 @@
import {Logger} from 'flipper-common';
import {createContext, useContext} from 'react';
import {stubLogger} from './Logger';
import {_stubLogger} from 'flipper-plugin-core';
export const _LoggerContext = createContext<Logger>(stubLogger);
export const _LoggerContext = createContext<Logger>(_stubLogger);
/**
* Provides the default logger that can be used for console logging,

View File

@@ -9,6 +9,9 @@
"references": [
{
"path": "../flipper-common"
},
{
"path": "../flipper-plugin-core"
}
]
}

View File

@@ -12,7 +12,7 @@
"dependencies": {
"flipper-common": "0.0.0",
"flipper-frontend-core": "0.0.0",
"flipper-plugin": "0.0.0",
"flipper-plugin-core": "0.0.0",
"immer": "^9.0.12"
},
"devDependencies": {

View File

@@ -13,7 +13,7 @@ import {
ClientConnection,
BaseDevice,
} from 'flipper-frontend-core';
import {_SandyPluginDefinition} from 'flipper-plugin';
import {_SandyPluginDefinition} from 'flipper-plugin-core';
export class HeadlessClient extends AbstractClient {
constructor(

View File

@@ -17,7 +17,7 @@ import {
getRenderHostInstance,
isSandyPlugin,
} from 'flipper-frontend-core';
import {_SandyPluginDefinition} from 'flipper-plugin';
import {_SandyPluginDefinition} from 'flipper-plugin-core';
export class HeadlessPluginInitializer extends AbstractPluginInitializer {
protected async getFlipperVersion() {

View File

@@ -18,8 +18,8 @@ import {
FlipperCompanionAvailablePlugin,
} from 'flipper-common';
import {BaseDevice} from 'flipper-frontend-core';
import {_SandyPluginDefinition} from 'flipper-plugin';
import {isAtom} from 'flipper-plugin';
import {_SandyPluginDefinition} from 'flipper-plugin-core';
import {isAtom} from 'flipper-plugin-core';
import {HeadlessClient} from './HeadlessClient';
import {FlipperServerCompanionEnv} from './init';

View File

@@ -9,7 +9,7 @@
import {FlipperServer, getLogger} from 'flipper-common';
import {getRenderHostInstance, setGlobalObject} from 'flipper-frontend-core';
import * as FlipperPluginSDK from 'flipper-plugin';
import * as FlipperPluginSDK from 'flipper-plugin-core';
import * as Immer from 'immer';
import {HeadlessPluginInitializer} from './HeadlessPluginInitializer';
import {initializeFlipperLibImplementation} from './initializeFlipperLibImplementation';

View File

@@ -7,7 +7,7 @@
* @format
*/
import {Logger, _setFlipperLibImplementation} from 'flipper-plugin';
import {Logger, _setFlipperLibImplementation} from 'flipper-plugin-core';
import {baseFlipperLibImplementation, RenderHost} from 'flipper-frontend-core';
export function initializeFlipperLibImplementation(

View File

@@ -13,7 +13,7 @@
"path": "../flipper-frontend-core"
},
{
"path": "../flipper-plugin"
"path": "../flipper-plugin-core"
}
]
}

View File

@@ -19,7 +19,7 @@
"axios": "^0.26.0",
"flipper-common": "0.0.0",
"flipper-doctor": "0.0.0",
"flipper-plugin": "0.0.0",
"flipper-plugin-core": "0.0.0",
"flipper-plugin-lib": "0.0.0",
"flipper-server-companion": "0.0.0",
"form-data": "^4.0.0",

View File

@@ -7,7 +7,7 @@
* @format
*/
import {DeviceLogEntry} from 'flipper-plugin';
import {DeviceLogEntry} from 'flipper-plugin-core';
import {parseAndroidCrash, shouldParseAndroidLog} from '../AndroidCrashUtils';
function getAndroidLog(

View File

@@ -12,9 +12,7 @@ import {
ServerAddOnStartDetails,
} from 'flipper-common';
import {assertNotNull} from '../comms/Utilities';
// Special subset of flipper-plugin exports designed for server-side usage
// eslint-disable-next-line no-restricted-imports
import * as FlipperPluginSDK from 'flipper-plugin/src/server';
import * as FlipperPluginSDK from 'flipper-plugin-core';
declare global {
// eslint-disable-next-line no-var

View File

@@ -19,7 +19,7 @@
"path": "../flipper-common"
},
{
"path": "../flipper-plugin"
"path": "../flipper-plugin-core"
},
{
"path": "../flipper-server-companion"

View File

@@ -23,7 +23,7 @@ import {homedir} from 'os';
const uiSourceDirs = [
'flipper-ui-browser',
'flipper-ui-core',
'flipper-plugin',
'flipper-plugin-core',
'flipper-common',
];

View File

@@ -182,6 +182,7 @@
"flipper-common",
"flipper-frontend-core",
"flipper-plugin",
"flipper-plugin-core",
"flipper-server-companion",
"flipper-server-core",
"flipper-ui-core",

View File

@@ -18,6 +18,9 @@
{
"path": "../flipper-plugin"
},
{
"path": "../flipper-plugin-core"
},
{
"path": "../flipper-server"
},