Reload single plugin on auto-update
Summary: Implemented a way for re-loading single plugin on auto-update. This make it possible to apply update without full Flipper restart. Reviewed By: mweststrate Differential Revision: D23729972 fbshipit-source-id: ed30f7cde5a0537945db0b5bb6969ae8fde42cb6
This commit is contained in:
committed by
Facebook GitHub Bot
parent
5e979403a0
commit
0982dc06a0
@@ -268,8 +268,8 @@ export default class Client extends EventEmitter {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
supportsPlugin(Plugin: ClientPluginDefinition): boolean {
|
supportsPlugin(pluginId: string): boolean {
|
||||||
return this.plugins.includes(Plugin.id);
|
return this.plugins.includes(pluginId);
|
||||||
}
|
}
|
||||||
|
|
||||||
isBackgroundPlugin(pluginId: string) {
|
isBackgroundPlugin(pluginId: string) {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import dispatcher, {
|
|||||||
getDynamicPlugins,
|
getDynamicPlugins,
|
||||||
checkDisabled,
|
checkDisabled,
|
||||||
checkGK,
|
checkGK,
|
||||||
requirePlugin,
|
createRequirePluginFunction,
|
||||||
filterNewestVersionOfEachPlugin,
|
filterNewestVersionOfEachPlugin,
|
||||||
} from '../plugins';
|
} from '../plugins';
|
||||||
import {PluginDetails} from 'flipper-plugin-lib';
|
import {PluginDetails} from 'flipper-plugin-lib';
|
||||||
@@ -136,7 +136,7 @@ test('checkGK for failing plugin', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('requirePlugin returns null for invalid requires', () => {
|
test('requirePlugin returns null for invalid requires', () => {
|
||||||
const requireFn = requirePlugin([], {}, require);
|
const requireFn = createRequirePluginFunction([], require);
|
||||||
const plugin = requireFn({
|
const plugin = requireFn({
|
||||||
...samplePluginDetails,
|
...samplePluginDetails,
|
||||||
name: 'pluginID',
|
name: 'pluginID',
|
||||||
@@ -149,7 +149,7 @@ test('requirePlugin returns null for invalid requires', () => {
|
|||||||
|
|
||||||
test('requirePlugin loads plugin', () => {
|
test('requirePlugin loads plugin', () => {
|
||||||
const name = 'pluginID';
|
const name = 'pluginID';
|
||||||
const requireFn = requirePlugin([], {}, require);
|
const requireFn = createRequirePluginFunction([], require);
|
||||||
const plugin = requireFn({
|
const plugin = requireFn({
|
||||||
...samplePluginDetails,
|
...samplePluginDetails,
|
||||||
name,
|
name,
|
||||||
@@ -236,7 +236,7 @@ test('bundled versions are used when env var FLIPPER_DISABLE_PLUGIN_AUTO_UPDATE
|
|||||||
|
|
||||||
test('requirePlugin loads valid Sandy plugin', () => {
|
test('requirePlugin loads valid Sandy plugin', () => {
|
||||||
const name = 'pluginID';
|
const name = 'pluginID';
|
||||||
const requireFn = requirePlugin([], {}, require);
|
const requireFn = createRequirePluginFunction([], require);
|
||||||
const plugin = requireFn({
|
const plugin = requireFn({
|
||||||
...samplePluginDetails,
|
...samplePluginDetails,
|
||||||
name,
|
name,
|
||||||
@@ -270,7 +270,7 @@ test('requirePlugin loads valid Sandy plugin', () => {
|
|||||||
test('requirePlugin errors on invalid Sandy plugin', () => {
|
test('requirePlugin errors on invalid Sandy plugin', () => {
|
||||||
const name = 'pluginID';
|
const name = 'pluginID';
|
||||||
const failedPlugins: any[] = [];
|
const failedPlugins: any[] = [];
|
||||||
const requireFn = requirePlugin(failedPlugins, {}, require);
|
const requireFn = createRequirePluginFunction(failedPlugins, require);
|
||||||
requireFn({
|
requireFn({
|
||||||
...samplePluginDetails,
|
...samplePluginDetails,
|
||||||
name,
|
name,
|
||||||
@@ -286,7 +286,7 @@ test('requirePlugin errors on invalid Sandy plugin', () => {
|
|||||||
|
|
||||||
test('requirePlugin loads valid Sandy Device plugin', () => {
|
test('requirePlugin loads valid Sandy Device plugin', () => {
|
||||||
const name = 'pluginID';
|
const name = 'pluginID';
|
||||||
const requireFn = requirePlugin([], {}, require);
|
const requireFn = createRequirePluginFunction([], require);
|
||||||
const plugin = requireFn({
|
const plugin = requireFn({
|
||||||
...samplePluginDetails,
|
...samplePluginDetails,
|
||||||
name,
|
name,
|
||||||
|
|||||||
@@ -37,7 +37,9 @@ import loadDynamicPlugins from '../utils/loadDynamicPlugins';
|
|||||||
import Immer from 'immer';
|
import Immer from 'immer';
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-unresolved
|
// eslint-disable-next-line import/no-unresolved
|
||||||
import getPluginIndex from '../utils/getDefaultPluginsIndex';
|
import getDefaultPluginsIndex from '../utils/getDefaultPluginsIndex';
|
||||||
|
|
||||||
|
let defaultPluginsIndex: any = null;
|
||||||
|
|
||||||
export default async (store: Store, logger: Logger) => {
|
export default async (store: Store, logger: Logger) => {
|
||||||
// expose Flipper and exact globally for dynamically loaded plugins
|
// expose Flipper and exact globally for dynamically loaded plugins
|
||||||
@@ -53,7 +55,7 @@ export default async (store: Store, logger: Logger) => {
|
|||||||
const disabledPlugins: Array<PluginDetails> = [];
|
const disabledPlugins: Array<PluginDetails> = [];
|
||||||
const failedPlugins: Array<[PluginDetails, string]> = [];
|
const failedPlugins: Array<[PluginDetails, string]> = [];
|
||||||
|
|
||||||
const defaultPluginsIndex = getPluginIndex();
|
defaultPluginsIndex = getDefaultPluginsIndex();
|
||||||
|
|
||||||
const initialPlugins: PluginDefinition[] = filterNewestVersionOfEachPlugin(
|
const initialPlugins: PluginDefinition[] = filterNewestVersionOfEachPlugin(
|
||||||
getBundledPlugins(),
|
getBundledPlugins(),
|
||||||
@@ -62,7 +64,7 @@ export default async (store: Store, logger: Logger) => {
|
|||||||
.map(reportVersion)
|
.map(reportVersion)
|
||||||
.filter(checkDisabled(disabledPlugins))
|
.filter(checkDisabled(disabledPlugins))
|
||||||
.filter(checkGK(gatekeepedPlugins))
|
.filter(checkGK(gatekeepedPlugins))
|
||||||
.map(requirePlugin(failedPlugins, defaultPluginsIndex))
|
.map(createRequirePluginFunction(failedPlugins))
|
||||||
.filter(notNull);
|
.filter(notNull);
|
||||||
|
|
||||||
store.dispatch(addGatekeepedPlugins(gatekeepedPlugins));
|
store.dispatch(addGatekeepedPlugins(gatekeepedPlugins));
|
||||||
@@ -173,18 +175,13 @@ export const checkDisabled = (disabledPlugins: Array<PluginDetails>) => (
|
|||||||
return !disabledList.has(plugin.name);
|
return !disabledList.has(plugin.name);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const requirePlugin = (
|
export const createRequirePluginFunction = (
|
||||||
failedPlugins: Array<[PluginDetails, string]>,
|
failedPlugins: Array<[PluginDetails, string]>,
|
||||||
defaultPluginsIndex: any,
|
|
||||||
reqFn: Function = global.electronRequire,
|
reqFn: Function = global.electronRequire,
|
||||||
) => {
|
) => {
|
||||||
return (pluginDetails: PluginDetails): PluginDefinition | null => {
|
return (pluginDetails: PluginDetails): PluginDefinition | null => {
|
||||||
try {
|
try {
|
||||||
return tryCatchReportPluginFailures(
|
return requirePlugin(pluginDetails, reqFn);
|
||||||
() => requirePluginInternal(pluginDetails, defaultPluginsIndex, reqFn),
|
|
||||||
'plugin:load',
|
|
||||||
pluginDetails.id,
|
|
||||||
);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
failedPlugins.push([pluginDetails, e.message]);
|
failedPlugins.push([pluginDetails, e.message]);
|
||||||
console.error(`Plugin ${pluginDetails.id} failed to load`, e);
|
console.error(`Plugin ${pluginDetails.id} failed to load`, e);
|
||||||
@@ -193,15 +190,24 @@ export const requirePlugin = (
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const requirePlugin = (
|
||||||
|
pluginDetails: PluginDetails,
|
||||||
|
reqFn: Function = global.electronRequire,
|
||||||
|
): PluginDefinition => {
|
||||||
|
return tryCatchReportPluginFailures(
|
||||||
|
() => requirePluginInternal(pluginDetails, reqFn),
|
||||||
|
'plugin:load',
|
||||||
|
pluginDetails.id,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const requirePluginInternal = (
|
const requirePluginInternal = (
|
||||||
pluginDetails: PluginDetails,
|
pluginDetails: PluginDetails,
|
||||||
defaultPluginsIndex: any,
|
|
||||||
reqFn: Function = global.electronRequire,
|
reqFn: Function = global.electronRequire,
|
||||||
) => {
|
): PluginDefinition => {
|
||||||
let plugin = pluginDetails.isDefault
|
let plugin = pluginDetails.isDefault
|
||||||
? defaultPluginsIndex[pluginDetails.name]
|
? defaultPluginsIndex[pluginDetails.name]
|
||||||
: reqFn(pluginDetails.entry);
|
: reqFn(pluginDetails.entry);
|
||||||
|
|
||||||
if (pluginDetails.flipperSDKVersion) {
|
if (pluginDetails.flipperSDKVersion) {
|
||||||
// Sandy plugin
|
// Sandy plugin
|
||||||
return new SandyPluginDefinition(pluginDetails, plugin);
|
return new SandyPluginDefinition(pluginDetails, plugin);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {State, addNotification} from '../notifications';
|
import {State, addNotification, removeNotification} from '../notifications';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
default as reducer,
|
default as reducer,
|
||||||
@@ -108,6 +108,56 @@ test('reduce setActiveNotifications', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('addNotification removes duplicates', () => {
|
test('addNotification removes duplicates', () => {
|
||||||
|
let res = reducer(
|
||||||
|
getInitialState(),
|
||||||
|
addNotification({
|
||||||
|
pluginId: 'test',
|
||||||
|
client: null,
|
||||||
|
notification,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
res = reducer(
|
||||||
|
res,
|
||||||
|
addNotification({
|
||||||
|
pluginId: 'test',
|
||||||
|
client: null,
|
||||||
|
notification: {
|
||||||
|
...notification,
|
||||||
|
id: 'otherId',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
res = reducer(
|
||||||
|
res,
|
||||||
|
removeNotification({
|
||||||
|
pluginId: 'test',
|
||||||
|
client: null,
|
||||||
|
notificationId: 'id',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
expect(res).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"activeNotifications": Array [
|
||||||
|
Object {
|
||||||
|
"client": null,
|
||||||
|
"notification": Object {
|
||||||
|
"id": "otherId",
|
||||||
|
"message": "message",
|
||||||
|
"severity": "warning",
|
||||||
|
"title": "title",
|
||||||
|
},
|
||||||
|
"pluginId": "test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"blacklistedCategories": Array [],
|
||||||
|
"blacklistedPlugins": Array [],
|
||||||
|
"clearedNotifications": Set {},
|
||||||
|
"invalidatedNotifications": Array [],
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('reduce removeNotification', () => {
|
||||||
let res = reducer(
|
let res = reducer(
|
||||||
getInitialState(),
|
getInitialState(),
|
||||||
addNotification({
|
addNotification({
|
||||||
|
|||||||
@@ -125,7 +125,12 @@ export type Action =
|
|||||||
type: 'SELECT_CLIENT';
|
type: 'SELECT_CLIENT';
|
||||||
payload: string;
|
payload: string;
|
||||||
}
|
}
|
||||||
| RegisterPluginAction;
|
| RegisterPluginAction
|
||||||
|
| {
|
||||||
|
// Implemented by rootReducer in `store.tsx`
|
||||||
|
type: 'UPDATE_PLUGIN';
|
||||||
|
payload: PluginDefinition;
|
||||||
|
};
|
||||||
|
|
||||||
const DEFAULT_PLUGIN = 'DeviceLogs';
|
const DEFAULT_PLUGIN = 'DeviceLogs';
|
||||||
const DEFAULT_DEVICE_BLACKLIST = [MacDevice];
|
const DEFAULT_DEVICE_BLACKLIST = [MacDevice];
|
||||||
@@ -341,7 +346,6 @@ export default (state: State = INITAL_STATE, action: Actions): State => {
|
|||||||
});
|
});
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
@@ -395,6 +399,11 @@ export const selectClient = (clientId: string): Action => ({
|
|||||||
payload: clientId,
|
payload: clientId,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const registerPluginUpdate = (payload: PluginDefinition): Action => ({
|
||||||
|
type: 'UPDATE_PLUGIN',
|
||||||
|
payload,
|
||||||
|
});
|
||||||
|
|
||||||
export function getAvailableClients(
|
export function getAvailableClients(
|
||||||
device: null | undefined | BaseDevice,
|
device: null | undefined | BaseDevice,
|
||||||
clients: Client[],
|
clients: Client[],
|
||||||
|
|||||||
@@ -16,6 +16,12 @@ export type PluginNotification = {
|
|||||||
client: null | string;
|
client: null | string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type PluginNotificationReference = {
|
||||||
|
notificationId: string;
|
||||||
|
pluginId: string;
|
||||||
|
client: null | string;
|
||||||
|
};
|
||||||
|
|
||||||
export type State = {
|
export type State = {
|
||||||
activeNotifications: Array<PluginNotification>;
|
activeNotifications: Array<PluginNotification>;
|
||||||
invalidatedNotifications: Array<PluginNotification>;
|
invalidatedNotifications: Array<PluginNotification>;
|
||||||
@@ -56,6 +62,10 @@ export type Action =
|
|||||||
| {
|
| {
|
||||||
type: 'ADD_NOTIFICATION';
|
type: 'ADD_NOTIFICATION';
|
||||||
payload: PluginNotification;
|
payload: PluginNotification;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'REMOVE_NOTIFICATION';
|
||||||
|
payload: PluginNotificationReference;
|
||||||
};
|
};
|
||||||
|
|
||||||
const INITIAL_STATE: State = {
|
const INITIAL_STATE: State = {
|
||||||
@@ -113,6 +123,18 @@ export default function reducer(
|
|||||||
action.payload,
|
action.payload,
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
case 'REMOVE_NOTIFICATION':
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
activeNotifications: [
|
||||||
|
...state.activeNotifications.filter(
|
||||||
|
(notif) =>
|
||||||
|
notif.client !== action.payload.client ||
|
||||||
|
notif.pluginId !== action.payload.pluginId ||
|
||||||
|
notif.notification.id !== action.payload.notificationId,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
};
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
@@ -166,6 +188,15 @@ export function addNotification(payload: PluginNotification): Action {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function removeNotification(
|
||||||
|
payload: PluginNotificationReference,
|
||||||
|
): Action {
|
||||||
|
return {
|
||||||
|
type: 'REMOVE_NOTIFICATION',
|
||||||
|
payload,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function addErrorNotification(
|
export function addErrorNotification(
|
||||||
title: string,
|
title: string,
|
||||||
message: string,
|
message: string,
|
||||||
|
|||||||
@@ -15,7 +15,11 @@ import produce from 'immer';
|
|||||||
import {
|
import {
|
||||||
defaultEnabledBackgroundPlugins,
|
defaultEnabledBackgroundPlugins,
|
||||||
getPluginKey,
|
getPluginKey,
|
||||||
|
isDevicePluginDefinition,
|
||||||
} from './utils/pluginUtils';
|
} from './utils/pluginUtils';
|
||||||
|
import Client from './Client';
|
||||||
|
import {PluginDefinition} from './plugin';
|
||||||
|
import {deconstructPluginKey} from './utils/clientUtils';
|
||||||
|
|
||||||
export const store: Store = createStore<StoreState, Actions, any, any>(
|
export const store: Store = createStore<StoreState, Actions, any, any>(
|
||||||
rootReducer,
|
rootReducer,
|
||||||
@@ -48,35 +52,59 @@ export function rootReducer(
|
|||||||
plugins.push(selectedPlugin);
|
plugins.push(selectedPlugin);
|
||||||
// enabling a plugin on one device enables it on all...
|
// enabling a plugin on one device enables it on all...
|
||||||
clients.forEach((client) => {
|
clients.forEach((client) => {
|
||||||
// sandy plugin? initialize it
|
startPlugin(client, plugin);
|
||||||
client.startPluginIfNeeded(plugin, true);
|
|
||||||
// background plugin? connect it needed
|
|
||||||
if (
|
|
||||||
!defaultEnabledBackgroundPlugins.includes(selectedPlugin) &&
|
|
||||||
client?.isBackgroundPlugin(selectedPlugin)
|
|
||||||
) {
|
|
||||||
client.initPlugin(selectedPlugin);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
plugins.splice(idx, 1);
|
plugins.splice(idx, 1);
|
||||||
// enabling a plugin on one device disables it on all...
|
// enabling a plugin on one device disables it on all...
|
||||||
clients.forEach((client) => {
|
clients.forEach((client) => {
|
||||||
// disconnect background plugins
|
stopPlugin(client, plugin.id);
|
||||||
if (
|
const pluginKey = getPluginKey(
|
||||||
!defaultEnabledBackgroundPlugins.includes(selectedPlugin) &&
|
client.id,
|
||||||
client?.isBackgroundPlugin(selectedPlugin)
|
{serial: client.query.device_id},
|
||||||
) {
|
plugin.id,
|
||||||
client.deinitPlugin(selectedPlugin);
|
);
|
||||||
}
|
delete draft.pluginMessageQueue[pluginKey];
|
||||||
// stop sandy plugins
|
|
||||||
client.stopPluginIfNeeded(plugin.id);
|
|
||||||
delete draft.pluginMessageQueue[
|
|
||||||
getPluginKey(client.id, {serial: client.query.device_id}, plugin.id)
|
|
||||||
];
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else if (action.type === 'UPDATE_PLUGIN' && state) {
|
||||||
|
const plugin: PluginDefinition = action.payload;
|
||||||
|
const clients = state.connections.clients;
|
||||||
|
return produce(state, (draft) => {
|
||||||
|
const clientsWithEnabledPlugin = clients.filter((c) => {
|
||||||
|
return (
|
||||||
|
c.supportsPlugin(plugin.id) &&
|
||||||
|
state.connections.userStarredPlugins[c.query.app]?.includes(plugin.id)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
// stop plugin for each client where it is enabled
|
||||||
|
clientsWithEnabledPlugin.forEach((client) => {
|
||||||
|
stopPlugin(client, plugin.id, true);
|
||||||
|
delete draft.pluginMessageQueue[
|
||||||
|
getPluginKey(client.id, {serial: client.query.device_id}, plugin.id)
|
||||||
|
];
|
||||||
|
});
|
||||||
|
// cleanup classic plugin state
|
||||||
|
Object.keys(draft.pluginStates).forEach((pluginKey) => {
|
||||||
|
const pluginKeyParts = deconstructPluginKey(pluginKey);
|
||||||
|
if (pluginKeyParts.pluginName === plugin.id) {
|
||||||
|
delete draft.pluginStates[pluginKey];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// update plugin definition
|
||||||
|
const {devicePlugins, clientPlugins} = draft.plugins;
|
||||||
|
const p = action.payload;
|
||||||
|
if (isDevicePluginDefinition(p)) {
|
||||||
|
devicePlugins.set(p.id, p);
|
||||||
|
} else {
|
||||||
|
clientPlugins.set(p.id, p);
|
||||||
|
}
|
||||||
|
// start plugin for each client
|
||||||
|
clientsWithEnabledPlugin.forEach((client) => {
|
||||||
|
startPlugin(client, plugin, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// otherwise
|
// otherwise
|
||||||
@@ -88,3 +116,36 @@ if (!isProduction()) {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
window.flipperStore = store;
|
window.flipperStore = store;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function stopPlugin(
|
||||||
|
client: Client,
|
||||||
|
pluginId: string,
|
||||||
|
forceInitBackgroundPlugin: boolean = false,
|
||||||
|
): boolean {
|
||||||
|
if (
|
||||||
|
(forceInitBackgroundPlugin ||
|
||||||
|
!defaultEnabledBackgroundPlugins.includes(pluginId)) &&
|
||||||
|
client?.isBackgroundPlugin(pluginId)
|
||||||
|
) {
|
||||||
|
client.deinitPlugin(pluginId);
|
||||||
|
}
|
||||||
|
// stop sandy plugins
|
||||||
|
client.stopPluginIfNeeded(pluginId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function startPlugin(
|
||||||
|
client: Client,
|
||||||
|
plugin: PluginDefinition,
|
||||||
|
forceInitBackgroundPlugin: boolean = false,
|
||||||
|
) {
|
||||||
|
client.startPluginIfNeeded(plugin, true);
|
||||||
|
// background plugin? connect it needed
|
||||||
|
if (
|
||||||
|
(forceInitBackgroundPlugin ||
|
||||||
|
!defaultEnabledBackgroundPlugins.includes(plugin.id)) &&
|
||||||
|
client?.isBackgroundPlugin(plugin.id)
|
||||||
|
) {
|
||||||
|
client.initPlugin(plugin.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export type NpmHostedPluginsSearchArgs = {
|
|||||||
|
|
||||||
export async function getNpmHostedPlugins(
|
export async function getNpmHostedPlugins(
|
||||||
args: NpmHostedPluginsSearchArgs = {},
|
args: NpmHostedPluginsSearchArgs = {},
|
||||||
): Promise<NpmPackageDescriptor[]> {
|
): Promise<readonly NpmPackageDescriptor[]> {
|
||||||
const index = provideSearchIndex();
|
const index = provideSearchIndex();
|
||||||
args = Object.assign(
|
args = Object.assign(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ async function installPluginFromTempDir(
|
|||||||
}
|
}
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
return pluginDetails;
|
return await getPluginDetails(destinationDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getPluginRootDir(dir: string) {
|
async function getPluginRootDir(dir: string) {
|
||||||
|
|||||||
Reference in New Issue
Block a user