Make plugin loading async

Summary: This diff makes plugin loading async, which we'd need in a browser env (either because we'd use `import()` or we need to fetch the source and than eval it), and deals with all the fallout of that

Reviewed By: timur-valiev

Differential Revision: D32669995

fbshipit-source-id: 73babf38a6757c451b8200c3b320409f127b8b5b
This commit is contained in:
Michel Weststrate
2021-12-08 04:25:28 -08:00
committed by Facebook GitHub Bot
parent 64747dc417
commit de59bbedd2
20 changed files with 282 additions and 90 deletions

View File

@@ -75,6 +75,7 @@ export default (
});
}
let running = false;
const unsubscribeHandlePluginCommands = sideEffect(
store,
{
@@ -85,14 +86,49 @@ export default (
noTimeBudgetWarns: true, // These side effects are critical, so we're doing them with zero throttling and want to avoid unnecessary warns
},
(state) => state.pluginManager.pluginCommandsQueue,
processPluginCommandsQueue,
async (_queue: PluginCommand[], store: Store) => {
// To make sure all commands are running in order, and not kicking off parallel command
// processing when new commands arrive (sideEffect doesn't await)
// we keep the 'running' flag, and keep running in a loop until the commandQueue is empty,
// to make sure any commands that have arrived during execution are executed
if (running) {
return; // will be picked up in while(true) loop
}
running = true;
try {
while (true) {
const remaining = store.getState().pluginManager.pluginCommandsQueue;
if (!remaining.length) {
return; // done
}
await processPluginCommandsQueue(remaining, store);
store.dispatch(pluginCommandsProcessed(remaining.length));
}
} finally {
running = false;
}
},
);
return async () => {
unsubscribeHandlePluginCommands();
};
};
export function processPluginCommandsQueue(
export async function awaitPluginCommandQueueEmpty(store: Store) {
if (store.getState().pluginManager.pluginCommandsQueue.length === 0) {
return;
}
return new Promise<void>((resolve) => {
const unsubscribe = store.subscribe(() => {
if (store.getState().pluginManager.pluginCommandsQueue.length === 0) {
unsubscribe();
resolve();
}
});
});
}
async function processPluginCommandsQueue(
queue: PluginCommand[],
store: Store,
) {
@@ -100,7 +136,7 @@ export function processPluginCommandsQueue(
try {
switch (command.type) {
case 'LOAD_PLUGIN':
loadPlugin(store, command.payload);
await loadPlugin(store, command.payload);
break;
case 'UNINSTALL_PLUGIN':
uninstallPlugin(store, command.payload);
@@ -121,12 +157,11 @@ export function processPluginCommandsQueue(
console.error('Failed to process command', command);
}
}
store.dispatch(pluginCommandsProcessed(queue.length));
}
function loadPlugin(store: Store, payload: LoadPluginActionPayload) {
async function loadPlugin(store: Store, payload: LoadPluginActionPayload) {
try {
const plugin = requirePlugin(payload.plugin);
const plugin = await requirePlugin(payload.plugin);
const enablePlugin = payload.enable;
updatePlugin(store, {plugin, enablePlugin});
} catch (err) {