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:
committed by
Facebook GitHub Bot
parent
2090120cda
commit
97b8b8a1c4
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user