Separate interfaces for installed, bundled and downloadable plugins
Summary: I've re-designed interfaces describing plugins as I found that mental overhead working with them became too expensive because of slightly flawed design. However this cascaded changes in many files so you can see how extensively these interfaces used in our codebase. Before this change we had one interface PluginDetails which described three different entities: 1) plugins installed on the disk 2) plugins bundled into Flipper 3) plugins available on Marketplace. It's hard to use this "general" PluginDetails interface because of this as you always need to think about all three use cases everywhere. After this change we have 3 separate interfaces: InstalledPluginDetails, BundledPluginDetails and DownloadablePluginDetails and things became much type-safer now. Reviewed By: mweststrate Differential Revision: D25530383 fbshipit-source-id: b93593916a980c04e36dc6ffa168797645a0ff9c
This commit is contained in:
committed by
Facebook GitHub Bot
parent
efb82e80b5
commit
5383017299
@@ -139,7 +139,7 @@ class PluginDebugger extends Component<Props> {
|
|||||||
getRows(): Array<TableBodyRow> {
|
getRows(): Array<TableBodyRow> {
|
||||||
const rows: Array<TableBodyRow> = [];
|
const rows: Array<TableBodyRow> = [];
|
||||||
|
|
||||||
const externalPluginPath = (p: any) => (p.isDefault ? 'bundled' : p.entry);
|
const externalPluginPath = (p: any) => (p.isBundled ? 'bundled' : p.entry);
|
||||||
|
|
||||||
this.props.gatekeepedPlugins.forEach((plugin) =>
|
this.props.gatekeepedPlugins.forEach((plugin) =>
|
||||||
rows.push(
|
rows.push(
|
||||||
|
|||||||
@@ -39,7 +39,8 @@ const samplePluginDetails1: UpdatablePluginDetails = {
|
|||||||
id: 'Hello',
|
id: 'Hello',
|
||||||
title: 'Hello',
|
title: 'Hello',
|
||||||
description: 'World?',
|
description: 'World?',
|
||||||
isDefault: false,
|
isBundled: false,
|
||||||
|
isActivatable: true,
|
||||||
updateStatus: {
|
updateStatus: {
|
||||||
kind: 'not-installed',
|
kind: 'not-installed',
|
||||||
version: '0.1.0',
|
version: '0.1.0',
|
||||||
@@ -57,7 +58,8 @@ const samplePluginDetails2: UpdatablePluginDetails = {
|
|||||||
id: 'World',
|
id: 'World',
|
||||||
title: 'World',
|
title: 'World',
|
||||||
description: 'Hello?',
|
description: 'Hello?',
|
||||||
isDefault: false,
|
isBundled: false,
|
||||||
|
isActivatable: true,
|
||||||
updateStatus: {
|
updateStatus: {
|
||||||
kind: 'not-installed',
|
kind: 'not-installed',
|
||||||
version: '0.2.0',
|
version: '0.2.0',
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import dispatcher, {
|
|||||||
createRequirePluginFunction,
|
createRequirePluginFunction,
|
||||||
filterNewestVersionOfEachPlugin,
|
filterNewestVersionOfEachPlugin,
|
||||||
} from '../plugins';
|
} from '../plugins';
|
||||||
import {PluginDetails} from 'flipper-plugin-lib';
|
import {BundledPluginDetails, InstalledPluginDetails} from 'flipper-plugin-lib';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import {remote} from 'electron';
|
import {remote} from 'electron';
|
||||||
import {FlipperPlugin} from '../../plugin';
|
import {FlipperPlugin} from '../../plugin';
|
||||||
@@ -37,17 +37,23 @@ const mockStore = configureStore<State, {}>([])(
|
|||||||
);
|
);
|
||||||
const logger = getInstance();
|
const logger = getInstance();
|
||||||
|
|
||||||
const samplePluginDetails: PluginDetails = {
|
const sampleInstalledPluginDetails: InstalledPluginDetails = {
|
||||||
name: 'other Name',
|
name: 'other Name',
|
||||||
entry: './test/index.js',
|
|
||||||
version: '1.0.0',
|
version: '1.0.0',
|
||||||
specVersion: 2,
|
specVersion: 2,
|
||||||
main: 'dist/bundle.js',
|
main: 'dist/bundle.js',
|
||||||
dir: '/Users/mock/.flipper/thirdparty/flipper-plugin-sample',
|
|
||||||
source: 'src/index.js',
|
source: 'src/index.js',
|
||||||
id: 'Sample',
|
id: 'Sample',
|
||||||
title: 'Sample',
|
title: 'Sample',
|
||||||
isDefault: false,
|
dir: '/Users/mock/.flipper/thirdparty/flipper-plugin-sample',
|
||||||
|
entry: 'this/path/does not/exist',
|
||||||
|
isBundled: false,
|
||||||
|
isActivatable: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const sampleBundledPluginDetails: BundledPluginDetails = {
|
||||||
|
...sampleInstalledPluginDetails,
|
||||||
|
isBundled: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -80,18 +86,16 @@ test('checkDisabled', () => {
|
|||||||
|
|
||||||
expect(
|
expect(
|
||||||
disabled({
|
disabled({
|
||||||
...samplePluginDetails,
|
...sampleBundledPluginDetails,
|
||||||
name: 'other Name',
|
name: 'other Name',
|
||||||
entry: './test/index.js',
|
|
||||||
version: '1.0.0',
|
version: '1.0.0',
|
||||||
}),
|
}),
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
disabled({
|
disabled({
|
||||||
...samplePluginDetails,
|
...sampleBundledPluginDetails,
|
||||||
name: disabledPlugin,
|
name: disabledPlugin,
|
||||||
entry: './test/index.js',
|
|
||||||
version: '1.0.0',
|
version: '1.0.0',
|
||||||
}),
|
}),
|
||||||
).toBeFalsy();
|
).toBeFalsy();
|
||||||
@@ -100,9 +104,8 @@ test('checkDisabled', () => {
|
|||||||
test('checkGK for plugin without GK', () => {
|
test('checkGK for plugin without GK', () => {
|
||||||
expect(
|
expect(
|
||||||
checkGK([])({
|
checkGK([])({
|
||||||
...samplePluginDetails,
|
...sampleBundledPluginDetails,
|
||||||
name: 'pluginID',
|
name: 'pluginID',
|
||||||
entry: './test/index.js',
|
|
||||||
version: '1.0.0',
|
version: '1.0.0',
|
||||||
}),
|
}),
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
@@ -111,23 +114,21 @@ test('checkGK for plugin without GK', () => {
|
|||||||
test('checkGK for passing plugin', () => {
|
test('checkGK for passing plugin', () => {
|
||||||
expect(
|
expect(
|
||||||
checkGK([])({
|
checkGK([])({
|
||||||
...samplePluginDetails,
|
...sampleBundledPluginDetails,
|
||||||
name: 'pluginID',
|
name: 'pluginID',
|
||||||
gatekeeper: TEST_PASSING_GK,
|
gatekeeper: TEST_PASSING_GK,
|
||||||
entry: './test/index.js',
|
|
||||||
version: '1.0.0',
|
version: '1.0.0',
|
||||||
}),
|
}),
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('checkGK for failing plugin', () => {
|
test('checkGK for failing plugin', () => {
|
||||||
const gatekeepedPlugins: PluginDetails[] = [];
|
const gatekeepedPlugins: InstalledPluginDetails[] = [];
|
||||||
const name = 'pluginID';
|
const name = 'pluginID';
|
||||||
const plugins = checkGK(gatekeepedPlugins)({
|
const plugins = checkGK(gatekeepedPlugins)({
|
||||||
...samplePluginDetails,
|
...sampleBundledPluginDetails,
|
||||||
name,
|
name,
|
||||||
gatekeeper: TEST_FAILING_GK,
|
gatekeeper: TEST_FAILING_GK,
|
||||||
entry: './test/index.js',
|
|
||||||
version: '1.0.0',
|
version: '1.0.0',
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -138,8 +139,9 @@ test('checkGK for failing plugin', () => {
|
|||||||
test('requirePlugin returns null for invalid requires', () => {
|
test('requirePlugin returns null for invalid requires', () => {
|
||||||
const requireFn = createRequirePluginFunction([], require);
|
const requireFn = createRequirePluginFunction([], require);
|
||||||
const plugin = requireFn({
|
const plugin = requireFn({
|
||||||
...samplePluginDetails,
|
...sampleInstalledPluginDetails,
|
||||||
name: 'pluginID',
|
name: 'pluginID',
|
||||||
|
dir: '/Users/mock/.flipper/thirdparty/flipper-plugin-sample',
|
||||||
entry: 'this/path/does not/exist',
|
entry: 'this/path/does not/exist',
|
||||||
version: '1.0.0',
|
version: '1.0.0',
|
||||||
});
|
});
|
||||||
@@ -151,8 +153,9 @@ test('requirePlugin loads plugin', () => {
|
|||||||
const name = 'pluginID';
|
const name = 'pluginID';
|
||||||
const requireFn = createRequirePluginFunction([], require);
|
const requireFn = createRequirePluginFunction([], require);
|
||||||
const plugin = requireFn({
|
const plugin = requireFn({
|
||||||
...samplePluginDetails,
|
...sampleInstalledPluginDetails,
|
||||||
name,
|
name,
|
||||||
|
dir: '/Users/mock/.flipper/thirdparty/flipper-plugin-sample',
|
||||||
entry: path.join(__dirname, 'TestPlugin'),
|
entry: path.join(__dirname, 'TestPlugin'),
|
||||||
version: '1.0.0',
|
version: '1.0.0',
|
||||||
});
|
});
|
||||||
@@ -162,21 +165,33 @@ test('requirePlugin loads plugin', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('newest version of each plugin is used', () => {
|
test('newest version of each plugin is used', () => {
|
||||||
const bundledPlugins: PluginDetails[] = [
|
const bundledPlugins: BundledPluginDetails[] = [
|
||||||
{...samplePluginDetails, name: 'flipper-plugin-test1', version: '0.1.0'},
|
|
||||||
{
|
{
|
||||||
...samplePluginDetails,
|
...sampleBundledPluginDetails,
|
||||||
|
name: 'flipper-plugin-test1',
|
||||||
|
version: '0.1.0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...sampleBundledPluginDetails,
|
||||||
name: 'flipper-plugin-test2',
|
name: 'flipper-plugin-test2',
|
||||||
version: '0.1.0-alpha.201',
|
version: '0.1.0-alpha.201',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const installedPlugins: PluginDetails[] = [
|
const installedPlugins: InstalledPluginDetails[] = [
|
||||||
{
|
{
|
||||||
...samplePluginDetails,
|
...sampleInstalledPluginDetails,
|
||||||
name: 'flipper-plugin-test2',
|
name: 'flipper-plugin-test2',
|
||||||
version: '0.1.0-alpha.21',
|
version: '0.1.0-alpha.21',
|
||||||
|
dir: '/Users/mock/.flipper/thirdparty/flipper-plugin-test2',
|
||||||
|
entry: './test/index.js',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...sampleInstalledPluginDetails,
|
||||||
|
name: 'flipper-plugin-test1',
|
||||||
|
version: '0.10.0',
|
||||||
|
dir: '/Users/mock/.flipper/thirdparty/flipper-plugin-test1',
|
||||||
|
entry: './test/index.js',
|
||||||
},
|
},
|
||||||
{...samplePluginDetails, name: 'flipper-plugin-test1', version: '0.10.0'},
|
|
||||||
];
|
];
|
||||||
const filteredPlugins = filterNewestVersionOfEachPlugin(
|
const filteredPlugins = filterNewestVersionOfEachPlugin(
|
||||||
bundledPlugins,
|
bundledPlugins,
|
||||||
@@ -184,12 +199,14 @@ test('newest version of each plugin is used', () => {
|
|||||||
);
|
);
|
||||||
expect(filteredPlugins).toHaveLength(2);
|
expect(filteredPlugins).toHaveLength(2);
|
||||||
expect(filteredPlugins).toContainEqual({
|
expect(filteredPlugins).toContainEqual({
|
||||||
...samplePluginDetails,
|
...sampleInstalledPluginDetails,
|
||||||
name: 'flipper-plugin-test1',
|
name: 'flipper-plugin-test1',
|
||||||
version: '0.10.0',
|
version: '0.10.0',
|
||||||
|
dir: '/Users/mock/.flipper/thirdparty/flipper-plugin-test1',
|
||||||
|
entry: './test/index.js',
|
||||||
});
|
});
|
||||||
expect(filteredPlugins).toContainEqual({
|
expect(filteredPlugins).toContainEqual({
|
||||||
...samplePluginDetails,
|
...sampleBundledPluginDetails,
|
||||||
name: 'flipper-plugin-test2',
|
name: 'flipper-plugin-test2',
|
||||||
version: '0.1.0-alpha.201',
|
version: '0.1.0-alpha.201',
|
||||||
});
|
});
|
||||||
@@ -198,21 +215,33 @@ test('newest version of each plugin is used', () => {
|
|||||||
test('bundled versions are used when env var FLIPPER_DISABLE_PLUGIN_AUTO_UPDATE is set even if newer versions are installed', () => {
|
test('bundled versions are used when env var FLIPPER_DISABLE_PLUGIN_AUTO_UPDATE is set even if newer versions are installed', () => {
|
||||||
process.env.FLIPPER_DISABLE_PLUGIN_AUTO_UPDATE = 'true';
|
process.env.FLIPPER_DISABLE_PLUGIN_AUTO_UPDATE = 'true';
|
||||||
try {
|
try {
|
||||||
const bundledPlugins: PluginDetails[] = [
|
const bundledPlugins: BundledPluginDetails[] = [
|
||||||
{...samplePluginDetails, name: 'flipper-plugin-test1', version: '0.1.0'},
|
|
||||||
{
|
{
|
||||||
...samplePluginDetails,
|
...sampleBundledPluginDetails,
|
||||||
|
name: 'flipper-plugin-test1',
|
||||||
|
version: '0.1.0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...sampleBundledPluginDetails,
|
||||||
name: 'flipper-plugin-test2',
|
name: 'flipper-plugin-test2',
|
||||||
version: '0.1.0-alpha.21',
|
version: '0.1.0-alpha.21',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const installedPlugins: PluginDetails[] = [
|
const installedPlugins: InstalledPluginDetails[] = [
|
||||||
{
|
{
|
||||||
...samplePluginDetails,
|
...sampleInstalledPluginDetails,
|
||||||
name: 'flipper-plugin-test2',
|
name: 'flipper-plugin-test2',
|
||||||
version: '0.1.0-alpha.201',
|
version: '0.1.0-alpha.201',
|
||||||
|
dir: '/Users/mock/.flipper/thirdparty/flipper-plugin-test2',
|
||||||
|
entry: './test/index.js',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...sampleInstalledPluginDetails,
|
||||||
|
name: 'flipper-plugin-test1',
|
||||||
|
version: '0.10.0',
|
||||||
|
dir: '/Users/mock/.flipper/thirdparty/flipper-plugin-test1',
|
||||||
|
entry: './test/index.js',
|
||||||
},
|
},
|
||||||
{...samplePluginDetails, name: 'flipper-plugin-test1', version: '0.10.0'},
|
|
||||||
];
|
];
|
||||||
const filteredPlugins = filterNewestVersionOfEachPlugin(
|
const filteredPlugins = filterNewestVersionOfEachPlugin(
|
||||||
bundledPlugins,
|
bundledPlugins,
|
||||||
@@ -220,12 +249,12 @@ test('bundled versions are used when env var FLIPPER_DISABLE_PLUGIN_AUTO_UPDATE
|
|||||||
);
|
);
|
||||||
expect(filteredPlugins).toHaveLength(2);
|
expect(filteredPlugins).toHaveLength(2);
|
||||||
expect(filteredPlugins).toContainEqual({
|
expect(filteredPlugins).toContainEqual({
|
||||||
...samplePluginDetails,
|
...sampleBundledPluginDetails,
|
||||||
name: 'flipper-plugin-test1',
|
name: 'flipper-plugin-test1',
|
||||||
version: '0.1.0',
|
version: '0.1.0',
|
||||||
});
|
});
|
||||||
expect(filteredPlugins).toContainEqual({
|
expect(filteredPlugins).toContainEqual({
|
||||||
...samplePluginDetails,
|
...sampleBundledPluginDetails,
|
||||||
name: 'flipper-plugin-test2',
|
name: 'flipper-plugin-test2',
|
||||||
version: '0.1.0-alpha.21',
|
version: '0.1.0-alpha.21',
|
||||||
});
|
});
|
||||||
@@ -238,8 +267,12 @@ test('requirePlugin loads valid Sandy plugin', () => {
|
|||||||
const name = 'pluginID';
|
const name = 'pluginID';
|
||||||
const requireFn = createRequirePluginFunction([], require);
|
const requireFn = createRequirePluginFunction([], require);
|
||||||
const plugin = requireFn({
|
const plugin = requireFn({
|
||||||
...samplePluginDetails,
|
...sampleInstalledPluginDetails,
|
||||||
name,
|
name,
|
||||||
|
dir: path.join(
|
||||||
|
__dirname,
|
||||||
|
'../../../../flipper-plugin/src/__tests__/TestPlugin',
|
||||||
|
),
|
||||||
entry: path.join(
|
entry: path.join(
|
||||||
__dirname,
|
__dirname,
|
||||||
'../../../../flipper-plugin/src/__tests__/TestPlugin',
|
'../../../../flipper-plugin/src/__tests__/TestPlugin',
|
||||||
@@ -253,7 +286,7 @@ test('requirePlugin loads valid Sandy plugin', () => {
|
|||||||
expect(plugin.details).toMatchObject({
|
expect(plugin.details).toMatchObject({
|
||||||
flipperSDKVersion: '0.0.0',
|
flipperSDKVersion: '0.0.0',
|
||||||
id: 'Sample',
|
id: 'Sample',
|
||||||
isDefault: false,
|
isBundled: false,
|
||||||
main: 'dist/bundle.js',
|
main: 'dist/bundle.js',
|
||||||
name: 'pluginID',
|
name: 'pluginID',
|
||||||
source: 'src/index.js',
|
source: 'src/index.js',
|
||||||
@@ -272,9 +305,10 @@ test('requirePlugin errors on invalid Sandy plugin', () => {
|
|||||||
const failedPlugins: any[] = [];
|
const failedPlugins: any[] = [];
|
||||||
const requireFn = createRequirePluginFunction(failedPlugins, require);
|
const requireFn = createRequirePluginFunction(failedPlugins, require);
|
||||||
requireFn({
|
requireFn({
|
||||||
...samplePluginDetails,
|
...sampleInstalledPluginDetails,
|
||||||
name,
|
name,
|
||||||
// Intentionally the wrong file:
|
// Intentionally the wrong file:
|
||||||
|
dir: __dirname,
|
||||||
entry: path.join(__dirname, 'TestPlugin'),
|
entry: path.join(__dirname, 'TestPlugin'),
|
||||||
version: '1.0.0',
|
version: '1.0.0',
|
||||||
flipperSDKVersion: '0.0.0',
|
flipperSDKVersion: '0.0.0',
|
||||||
@@ -288,8 +322,12 @@ test('requirePlugin loads valid Sandy Device plugin', () => {
|
|||||||
const name = 'pluginID';
|
const name = 'pluginID';
|
||||||
const requireFn = createRequirePluginFunction([], require);
|
const requireFn = createRequirePluginFunction([], require);
|
||||||
const plugin = requireFn({
|
const plugin = requireFn({
|
||||||
...samplePluginDetails,
|
...sampleInstalledPluginDetails,
|
||||||
name,
|
name,
|
||||||
|
dir: path.join(
|
||||||
|
__dirname,
|
||||||
|
'../../../../flipper-plugin/src/__tests__/DeviceTestPlugin',
|
||||||
|
),
|
||||||
entry: path.join(
|
entry: path.join(
|
||||||
__dirname,
|
__dirname,
|
||||||
'../../../../flipper-plugin/src/__tests__/DeviceTestPlugin',
|
'../../../../flipper-plugin/src/__tests__/DeviceTestPlugin',
|
||||||
@@ -303,7 +341,7 @@ test('requirePlugin loads valid Sandy Device plugin', () => {
|
|||||||
expect(plugin.details).toMatchObject({
|
expect(plugin.details).toMatchObject({
|
||||||
flipperSDKVersion: '0.0.0',
|
flipperSDKVersion: '0.0.0',
|
||||||
id: 'Sample',
|
id: 'Sample',
|
||||||
isDefault: false,
|
isBundled: false,
|
||||||
main: 'dist/bundle.js',
|
main: 'dist/bundle.js',
|
||||||
name: 'pluginID',
|
name: 'pluginID',
|
||||||
source: 'src/index.js',
|
source: 'src/index.js',
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
DownloadablePluginDetails,
|
DownloadablePluginDetails,
|
||||||
|
getInstalledPluginDetails,
|
||||||
|
getPluginVersionInstallationDir,
|
||||||
installPluginFromFile,
|
installPluginFromFile,
|
||||||
} from 'flipper-plugin-lib';
|
} from 'flipper-plugin-lib';
|
||||||
import {Store} from '../reducers/index';
|
import {Store} from '../reducers/index';
|
||||||
@@ -62,23 +64,24 @@ async function handlePluginDownload(
|
|||||||
store: Store,
|
store: Store,
|
||||||
) {
|
) {
|
||||||
const dispatch = store.dispatch;
|
const dispatch = store.dispatch;
|
||||||
const {name, title, version, downloadUrl, dir} = plugin;
|
const {name, title, version, downloadUrl} = plugin;
|
||||||
|
const installationDir = getPluginVersionInstallationDir(name, version);
|
||||||
console.log(
|
console.log(
|
||||||
`Downloading plugin "${title}" v${version} from "${downloadUrl}" to "${dir}".`,
|
`Downloading plugin "${title}" v${version} from "${downloadUrl}" to "${installationDir}".`,
|
||||||
);
|
);
|
||||||
const targetDir = await getTempDirName();
|
const tmpDir = await getTempDirName();
|
||||||
const targetFile = path.join(targetDir, `${name}-${version}.tgz`);
|
const tmpFile = path.join(tmpDir, `${name}-${version}.tgz`);
|
||||||
try {
|
try {
|
||||||
const cancellationSource = axios.CancelToken.source();
|
const cancellationSource = axios.CancelToken.source();
|
||||||
dispatch(
|
dispatch(
|
||||||
pluginDownloadStarted({plugin, cancel: cancellationSource.cancel}),
|
pluginDownloadStarted({plugin, cancel: cancellationSource.cancel}),
|
||||||
);
|
);
|
||||||
if (await fs.pathExists(dir)) {
|
if (await fs.pathExists(installationDir)) {
|
||||||
console.log(
|
console.log(
|
||||||
`Using existing files instead of downloading plugin "${title}" v${version} from "${downloadUrl}" to "${dir}"`,
|
`Using existing files instead of downloading plugin "${title}" v${version} from "${downloadUrl}" to "${installationDir}"`,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
await fs.ensureDir(targetDir);
|
await fs.ensureDir(tmpDir);
|
||||||
let percentCompleted = 0;
|
let percentCompleted = 0;
|
||||||
const response = await axios.get(plugin.downloadUrl, {
|
const response = await axios.get(plugin.downloadUrl, {
|
||||||
adapter: axiosHttpAdapter,
|
adapter: axiosHttpAdapter,
|
||||||
@@ -103,15 +106,16 @@ async function handlePluginDownload(
|
|||||||
}
|
}
|
||||||
const responseStream = response.data as fs.ReadStream;
|
const responseStream = response.data as fs.ReadStream;
|
||||||
const writeStream = responseStream.pipe(
|
const writeStream = responseStream.pipe(
|
||||||
fs.createWriteStream(targetFile, {autoClose: true}),
|
fs.createWriteStream(tmpFile, {autoClose: true}),
|
||||||
);
|
);
|
||||||
await new Promise((resolve, reject) =>
|
await new Promise((resolve, reject) =>
|
||||||
writeStream.once('finish', resolve).once('error', reject),
|
writeStream.once('finish', resolve).once('error', reject),
|
||||||
);
|
);
|
||||||
await installPluginFromFile(targetFile);
|
await installPluginFromFile(tmpFile);
|
||||||
}
|
}
|
||||||
|
const installedPlugin = await getInstalledPluginDetails(installationDir);
|
||||||
if (!store.getState().plugins.clientPlugins.has(plugin.id)) {
|
if (!store.getState().plugins.clientPlugins.has(plugin.id)) {
|
||||||
const pluginDefinition = requirePlugin(plugin);
|
const pluginDefinition = requirePlugin(installedPlugin);
|
||||||
dispatch(
|
dispatch(
|
||||||
registerPluginUpdate({
|
registerPluginUpdate({
|
||||||
plugin: pluginDefinition,
|
plugin: pluginDefinition,
|
||||||
@@ -120,11 +124,11 @@ async function handlePluginDownload(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
console.log(
|
console.log(
|
||||||
`Successfully downloaded and installed plugin "${title}" v${version} from "${downloadUrl}" to "${dir}".`,
|
`Successfully downloaded and installed plugin "${title}" v${version} from "${downloadUrl}" to "${installationDir}".`,
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(
|
console.error(
|
||||||
`Failed to download plugin "${title}" v${version} from "${downloadUrl}" to "${dir}".`,
|
`Failed to download plugin "${title}" v${version} from "${downloadUrl}" to "${installationDir}".`,
|
||||||
error,
|
error,
|
||||||
);
|
);
|
||||||
if (startedByUser) {
|
if (startedByUser) {
|
||||||
@@ -144,6 +148,6 @@ async function handlePluginDownload(
|
|||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
dispatch(pluginDownloadFinished({plugin}));
|
dispatch(pluginDownloadFinished({plugin}));
|
||||||
await fs.remove(targetDir);
|
await fs.remove(tmpDir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,11 @@ import isProduction from '../utils/isProduction';
|
|||||||
import {notNull} from '../utils/typeUtils';
|
import {notNull} from '../utils/typeUtils';
|
||||||
import {sideEffect} from '../utils/sideEffect';
|
import {sideEffect} from '../utils/sideEffect';
|
||||||
import semver from 'semver';
|
import semver from 'semver';
|
||||||
import {PluginDetails} from 'flipper-plugin-lib';
|
import {
|
||||||
|
ActivatablePluginDetails,
|
||||||
|
BundledPluginDetails,
|
||||||
|
InstalledPluginDetails,
|
||||||
|
} from 'flipper-plugin-lib';
|
||||||
import {tryCatchReportPluginFailures, reportUsage} from '../utils/metrics';
|
import {tryCatchReportPluginFailures, reportUsage} from '../utils/metrics';
|
||||||
import * as FlipperPluginSDK from 'flipper-plugin';
|
import * as FlipperPluginSDK from 'flipper-plugin';
|
||||||
import {_SandyPluginDefinition} from 'flipper-plugin';
|
import {_SandyPluginDefinition} from 'flipper-plugin';
|
||||||
@@ -51,9 +55,9 @@ export default async (store: Store, logger: Logger) => {
|
|||||||
globalObject.FlipperPlugin = FlipperPluginSDK;
|
globalObject.FlipperPlugin = FlipperPluginSDK;
|
||||||
globalObject.Immer = Immer;
|
globalObject.Immer = Immer;
|
||||||
|
|
||||||
const gatekeepedPlugins: Array<PluginDetails> = [];
|
const gatekeepedPlugins: Array<ActivatablePluginDetails> = [];
|
||||||
const disabledPlugins: Array<PluginDetails> = [];
|
const disabledPlugins: Array<ActivatablePluginDetails> = [];
|
||||||
const failedPlugins: Array<[PluginDetails, string]> = [];
|
const failedPlugins: Array<[ActivatablePluginDetails, string]> = [];
|
||||||
|
|
||||||
defaultPluginsIndex = getDefaultPluginsIndex();
|
defaultPluginsIndex = getDefaultPluginsIndex();
|
||||||
|
|
||||||
@@ -89,7 +93,7 @@ export default async (store: Store, logger: Logger) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
function reportVersion(pluginDetails: PluginDetails) {
|
function reportVersion(pluginDetails: ActivatablePluginDetails) {
|
||||||
reportUsage(
|
reportUsage(
|
||||||
'plugin:version',
|
'plugin:version',
|
||||||
{
|
{
|
||||||
@@ -101,10 +105,10 @@ function reportVersion(pluginDetails: PluginDetails) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function filterNewestVersionOfEachPlugin(
|
export function filterNewestVersionOfEachPlugin(
|
||||||
bundledPlugins: PluginDetails[],
|
bundledPlugins: BundledPluginDetails[],
|
||||||
dynamicPlugins: PluginDetails[],
|
dynamicPlugins: InstalledPluginDetails[],
|
||||||
): PluginDetails[] {
|
): ActivatablePluginDetails[] {
|
||||||
const pluginByName: {[key: string]: PluginDetails} = {};
|
const pluginByName: {[key: string]: ActivatablePluginDetails} = {};
|
||||||
for (const plugin of bundledPlugins) {
|
for (const plugin of bundledPlugins) {
|
||||||
pluginByName[plugin.name] = plugin;
|
pluginByName[plugin.name] = plugin;
|
||||||
}
|
}
|
||||||
@@ -120,7 +124,7 @@ export function filterNewestVersionOfEachPlugin(
|
|||||||
return Object.values(pluginByName);
|
return Object.values(pluginByName);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBundledPlugins(): Array<PluginDetails> {
|
function getBundledPlugins(): Array<BundledPluginDetails> {
|
||||||
// DefaultPlugins that are included in the bundle.
|
// DefaultPlugins that are included in the bundle.
|
||||||
// List of defaultPlugins is written at build time
|
// List of defaultPlugins is written at build time
|
||||||
const pluginPath =
|
const pluginPath =
|
||||||
@@ -129,7 +133,7 @@ function getBundledPlugins(): Array<PluginDetails> {
|
|||||||
? path.join(__dirname, 'defaultPlugins')
|
? path.join(__dirname, 'defaultPlugins')
|
||||||
: './defaultPlugins/index.json');
|
: './defaultPlugins/index.json');
|
||||||
|
|
||||||
let bundledPlugins: Array<PluginDetails> = [];
|
let bundledPlugins: Array<BundledPluginDetails> = [];
|
||||||
try {
|
try {
|
||||||
bundledPlugins = global.electronRequire(pluginPath);
|
bundledPlugins = global.electronRequire(pluginPath);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -148,8 +152,8 @@ export async function getDynamicPlugins() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const checkGK = (gatekeepedPlugins: Array<PluginDetails>) => (
|
export const checkGK = (gatekeepedPlugins: Array<ActivatablePluginDetails>) => (
|
||||||
plugin: PluginDetails,
|
plugin: ActivatablePluginDetails,
|
||||||
): boolean => {
|
): boolean => {
|
||||||
if (!plugin.gatekeeper) {
|
if (!plugin.gatekeeper) {
|
||||||
return true;
|
return true;
|
||||||
@@ -161,7 +165,9 @@ export const checkGK = (gatekeepedPlugins: Array<PluginDetails>) => (
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const checkDisabled = (disabledPlugins: Array<PluginDetails>) => {
|
export const checkDisabled = (
|
||||||
|
disabledPlugins: Array<ActivatablePluginDetails>,
|
||||||
|
) => {
|
||||||
const enabledList = process.env.FLIPPER_ENABLED_PLUGINS
|
const enabledList = process.env.FLIPPER_ENABLED_PLUGINS
|
||||||
? new Set<string>(process.env.FLIPPER_ENABLED_PLUGINS.split(','))
|
? new Set<string>(process.env.FLIPPER_ENABLED_PLUGINS.split(','))
|
||||||
: null;
|
: null;
|
||||||
@@ -171,7 +177,7 @@ export const checkDisabled = (disabledPlugins: Array<PluginDetails>) => {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
return (plugin: PluginDetails): boolean => {
|
return (plugin: ActivatablePluginDetails): boolean => {
|
||||||
if (disabledList.has(plugin.name)) {
|
if (disabledList.has(plugin.name)) {
|
||||||
disabledPlugins.push(plugin);
|
disabledPlugins.push(plugin);
|
||||||
return false;
|
return false;
|
||||||
@@ -192,10 +198,10 @@ export const checkDisabled = (disabledPlugins: Array<PluginDetails>) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const createRequirePluginFunction = (
|
export const createRequirePluginFunction = (
|
||||||
failedPlugins: Array<[PluginDetails, string]>,
|
failedPlugins: Array<[ActivatablePluginDetails, string]>,
|
||||||
reqFn: Function = global.electronRequire,
|
reqFn: Function = global.electronRequire,
|
||||||
) => {
|
) => {
|
||||||
return (pluginDetails: PluginDetails): PluginDefinition | null => {
|
return (pluginDetails: ActivatablePluginDetails): PluginDefinition | null => {
|
||||||
try {
|
try {
|
||||||
return requirePlugin(pluginDetails, reqFn);
|
return requirePlugin(pluginDetails, reqFn);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -207,7 +213,7 @@ export const createRequirePluginFunction = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const requirePlugin = (
|
export const requirePlugin = (
|
||||||
pluginDetails: PluginDetails,
|
pluginDetails: ActivatablePluginDetails,
|
||||||
reqFn: Function = global.electronRequire,
|
reqFn: Function = global.electronRequire,
|
||||||
): PluginDefinition => {
|
): PluginDefinition => {
|
||||||
return tryCatchReportPluginFailures(
|
return tryCatchReportPluginFailures(
|
||||||
@@ -218,10 +224,10 @@ export const requirePlugin = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
const requirePluginInternal = (
|
const requirePluginInternal = (
|
||||||
pluginDetails: PluginDetails,
|
pluginDetails: ActivatablePluginDetails,
|
||||||
reqFn: Function = global.electronRequire,
|
reqFn: Function = global.electronRequire,
|
||||||
): PluginDefinition => {
|
): PluginDefinition => {
|
||||||
let plugin = pluginDetails.isDefault
|
let plugin = pluginDetails.isBundled
|
||||||
? defaultPluginsIndex[pluginDetails.name]
|
? defaultPluginsIndex[pluginDetails.name]
|
||||||
: reqFn(pluginDetails.entry);
|
: reqFn(pluginDetails.entry);
|
||||||
if (pluginDetails.flipperSDKVersion) {
|
if (pluginDetails.flipperSDKVersion) {
|
||||||
@@ -248,7 +254,8 @@ const requirePluginInternal = (
|
|||||||
// set values from package.json as static variables on class
|
// set values from package.json as static variables on class
|
||||||
Object.keys(pluginDetails).forEach((key) => {
|
Object.keys(pluginDetails).forEach((key) => {
|
||||||
if (key !== 'name' && key !== 'id') {
|
if (key !== 'name' && key !== 'id') {
|
||||||
plugin[key] = plugin[key] || pluginDetails[key as keyof PluginDetails];
|
plugin[key] =
|
||||||
|
plugin[key] || pluginDetails[key as keyof ActivatablePluginDetails];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import {Idler} from './utils/Idler';
|
|||||||
import {StaticView} from './reducers/connections';
|
import {StaticView} from './reducers/connections';
|
||||||
import {State as ReduxState} from './reducers';
|
import {State as ReduxState} from './reducers';
|
||||||
import {DEFAULT_MAX_QUEUE_SIZE} from './reducers/pluginMessageQueue';
|
import {DEFAULT_MAX_QUEUE_SIZE} from './reducers/pluginMessageQueue';
|
||||||
import {PluginDetails} from 'flipper-plugin-lib';
|
import {ActivatablePluginDetails} from 'flipper-plugin-lib';
|
||||||
import {Settings} from './reducers/settings';
|
import {Settings} from './reducers/settings';
|
||||||
import {_SandyPluginDefinition} from 'flipper-plugin';
|
import {_SandyPluginDefinition} from 'flipper-plugin';
|
||||||
|
|
||||||
@@ -122,9 +122,8 @@ export abstract class FlipperBasePlugin<
|
|||||||
static version: string = '';
|
static version: string = '';
|
||||||
static icon: string | null = null;
|
static icon: string | null = null;
|
||||||
static gatekeeper: string | null = null;
|
static gatekeeper: string | null = null;
|
||||||
static entry: string | null = null;
|
static isBundled: boolean;
|
||||||
static isDefault: boolean;
|
static details: ActivatablePluginDetails;
|
||||||
static details: PluginDetails;
|
|
||||||
static keyboardActions: KeyboardActions | null;
|
static keyboardActions: KeyboardActions | null;
|
||||||
static screenshot: string | null;
|
static screenshot: string | null;
|
||||||
static defaultPersistedState: any;
|
static defaultPersistedState: any;
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ import createPaste from '../fb-stubs/createPaste';
|
|||||||
import {ReactNode} from 'react';
|
import {ReactNode} from 'react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {KeyboardActions} from '../MenuBar';
|
import {KeyboardActions} from '../MenuBar';
|
||||||
import {PluginDetails} from 'flipper-plugin-lib';
|
import {BundledPluginDetails} from 'flipper-plugin-lib';
|
||||||
|
|
||||||
type ID = string;
|
type ID = string;
|
||||||
|
|
||||||
@@ -256,7 +256,7 @@ export default function createTableNativePlugin(id: string, title: string) {
|
|||||||
static id = id || '';
|
static id = id || '';
|
||||||
static title = title || '';
|
static title = title || '';
|
||||||
|
|
||||||
static details: PluginDetails = {
|
static details: BundledPluginDetails = {
|
||||||
id,
|
id,
|
||||||
title,
|
title,
|
||||||
icon: 'apps',
|
icon: 'apps',
|
||||||
@@ -264,11 +264,10 @@ export default function createTableNativePlugin(id: string, title: string) {
|
|||||||
// all hmm...
|
// all hmm...
|
||||||
specVersion: 1,
|
specVersion: 1,
|
||||||
version: 'auto',
|
version: 'auto',
|
||||||
dir: '',
|
|
||||||
source: '',
|
source: '',
|
||||||
main: '',
|
main: '',
|
||||||
entry: '',
|
isBundled: true,
|
||||||
isDefault: true,
|
isActivatable: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultPersistedState: PersistedState = {
|
static defaultPersistedState: PersistedState = {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {default as reducer, registerInstalledPlugins} from '../pluginManager';
|
import {default as reducer, registerInstalledPlugins} from '../pluginManager';
|
||||||
import {PluginDetails} from 'flipper-plugin-lib';
|
import {InstalledPluginDetails} from 'flipper-plugin-lib';
|
||||||
|
|
||||||
test('reduce empty registerInstalledPlugins', () => {
|
test('reduce empty registerInstalledPlugins', () => {
|
||||||
const result = reducer(undefined, registerInstalledPlugins([]));
|
const result = reducer(undefined, registerInstalledPlugins([]));
|
||||||
@@ -22,12 +22,13 @@ const EXAMPLE_PLUGIN = {
|
|||||||
dir: '/plugins/test',
|
dir: '/plugins/test',
|
||||||
specVersion: 2,
|
specVersion: 2,
|
||||||
source: 'src/index.ts',
|
source: 'src/index.ts',
|
||||||
isDefault: false,
|
isBundled: false,
|
||||||
|
isActivatable: true,
|
||||||
main: 'lib/index.js',
|
main: 'lib/index.js',
|
||||||
title: 'test',
|
title: 'test',
|
||||||
id: 'test',
|
id: 'test',
|
||||||
entry: '/plugins/test/lib/index.js',
|
entry: '/plugins/test/lib/index.js',
|
||||||
} as PluginDetails;
|
} as InstalledPluginDetails;
|
||||||
|
|
||||||
test('reduce registerInstalledPlugins, clear again', () => {
|
test('reduce registerInstalledPlugins, clear again', () => {
|
||||||
const result = reducer(undefined, registerInstalledPlugins([EXAMPLE_PLUGIN]));
|
const result = reducer(undefined, registerInstalledPlugins([EXAMPLE_PLUGIN]));
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ test('add gatekeeped plugin', () => {
|
|||||||
dir: '/plugins/test',
|
dir: '/plugins/test',
|
||||||
specVersion: 2,
|
specVersion: 2,
|
||||||
source: 'src/index.ts',
|
source: 'src/index.ts',
|
||||||
isDefault: false,
|
isBundled: false,
|
||||||
main: 'lib/index.js',
|
main: 'lib/index.js',
|
||||||
title: 'test',
|
title: 'test',
|
||||||
id: 'test',
|
id: 'test',
|
||||||
|
|||||||
@@ -7,7 +7,10 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {DownloadablePluginDetails} from 'flipper-plugin-lib';
|
import {
|
||||||
|
DownloadablePluginDetails,
|
||||||
|
getPluginVersionInstallationDir,
|
||||||
|
} from 'flipper-plugin-lib';
|
||||||
import {Actions} from '.';
|
import {Actions} from '.';
|
||||||
import produce from 'immer';
|
import produce from 'immer';
|
||||||
import {Canceler} from 'axios';
|
import {Canceler} from 'axios';
|
||||||
@@ -66,18 +69,22 @@ export default function reducer(
|
|||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case 'PLUGIN_DOWNLOAD_START': {
|
case 'PLUGIN_DOWNLOAD_START': {
|
||||||
const {plugin, startedByUser} = action.payload;
|
const {plugin, startedByUser} = action.payload;
|
||||||
const downloadState = state[plugin.dir];
|
const installationDir = getPluginVersionInstallationDir(
|
||||||
|
plugin.name,
|
||||||
|
plugin.version,
|
||||||
|
);
|
||||||
|
const downloadState = state[installationDir];
|
||||||
if (downloadState) {
|
if (downloadState) {
|
||||||
// If download is already in progress - re-use the existing state.
|
// If download is already in progress - re-use the existing state.
|
||||||
return produce(state, (draft) => {
|
return produce(state, (draft) => {
|
||||||
draft[plugin.dir] = {
|
draft[installationDir] = {
|
||||||
...downloadState,
|
...downloadState,
|
||||||
startedByUser: startedByUser || downloadState.startedByUser,
|
startedByUser: startedByUser || downloadState.startedByUser,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return produce(state, (draft) => {
|
return produce(state, (draft) => {
|
||||||
draft[plugin.dir] = {
|
draft[installationDir] = {
|
||||||
plugin,
|
plugin,
|
||||||
startedByUser: startedByUser,
|
startedByUser: startedByUser,
|
||||||
status: PluginDownloadStatus.QUEUED,
|
status: PluginDownloadStatus.QUEUED,
|
||||||
@@ -86,15 +93,19 @@ export default function reducer(
|
|||||||
}
|
}
|
||||||
case 'PLUGIN_DOWNLOAD_STARTED': {
|
case 'PLUGIN_DOWNLOAD_STARTED': {
|
||||||
const {plugin, cancel} = action.payload;
|
const {plugin, cancel} = action.payload;
|
||||||
const downloadState = state[plugin.dir];
|
const installationDir = getPluginVersionInstallationDir(
|
||||||
|
plugin.name,
|
||||||
|
plugin.version,
|
||||||
|
);
|
||||||
|
const downloadState = state[installationDir];
|
||||||
if (downloadState?.status !== PluginDownloadStatus.QUEUED) {
|
if (downloadState?.status !== PluginDownloadStatus.QUEUED) {
|
||||||
console.warn(
|
console.warn(
|
||||||
`Invalid state transition PLUGIN_DOWNLOAD_STARTED in status ${downloadState?.status} for download to directory ${plugin.dir}.`,
|
`Invalid state transition PLUGIN_DOWNLOAD_STARTED in status ${downloadState?.status} for download to directory ${installationDir}.`,
|
||||||
);
|
);
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
return produce(state, (draft) => {
|
return produce(state, (draft) => {
|
||||||
draft[plugin.dir] = {
|
draft[installationDir] = {
|
||||||
status: PluginDownloadStatus.STARTED,
|
status: PluginDownloadStatus.STARTED,
|
||||||
plugin,
|
plugin,
|
||||||
startedByUser: downloadState.startedByUser,
|
startedByUser: downloadState.startedByUser,
|
||||||
@@ -104,8 +115,12 @@ export default function reducer(
|
|||||||
}
|
}
|
||||||
case 'PLUGIN_DOWNLOAD_FINISHED': {
|
case 'PLUGIN_DOWNLOAD_FINISHED': {
|
||||||
const {plugin} = action.payload;
|
const {plugin} = action.payload;
|
||||||
|
const installationDir = getPluginVersionInstallationDir(
|
||||||
|
plugin.name,
|
||||||
|
plugin.version,
|
||||||
|
);
|
||||||
return produce(state, (draft) => {
|
return produce(state, (draft) => {
|
||||||
delete draft[plugin.dir];
|
delete draft[installationDir];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -8,19 +8,19 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Actions} from './';
|
import {Actions} from './';
|
||||||
import {PluginDetails} from 'flipper-plugin-lib';
|
import {InstalledPluginDetails} from 'flipper-plugin-lib';
|
||||||
import {PluginDefinition} from '../plugin';
|
import {PluginDefinition} from '../plugin';
|
||||||
import {produce} from 'immer';
|
import {produce} from 'immer';
|
||||||
|
|
||||||
export type State = {
|
export type State = {
|
||||||
installedPlugins: PluginDetails[];
|
installedPlugins: InstalledPluginDetails[];
|
||||||
uninstalledPlugins: Set<string>;
|
uninstalledPlugins: Set<string>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Action =
|
export type Action =
|
||||||
| {
|
| {
|
||||||
type: 'REGISTER_INSTALLED_PLUGINS';
|
type: 'REGISTER_INSTALLED_PLUGINS';
|
||||||
payload: PluginDetails[];
|
payload: InstalledPluginDetails[];
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
// Implemented by rootReducer in `store.tsx`
|
// Implemented by rootReducer in `store.tsx`
|
||||||
@@ -48,7 +48,9 @@ export default function reducer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const registerInstalledPlugins = (payload: PluginDetails[]): Action => ({
|
export const registerInstalledPlugins = (
|
||||||
|
payload: InstalledPluginDetails[],
|
||||||
|
): Action => ({
|
||||||
type: 'REGISTER_INSTALLED_PLUGINS',
|
type: 'REGISTER_INSTALLED_PLUGINS',
|
||||||
payload,
|
payload,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -229,7 +229,7 @@ export const PluginList = memo(function PluginList({
|
|||||||
tooltip={getPluginTooltip(plugin.details)}
|
tooltip={getPluginTooltip(plugin.details)}
|
||||||
actions={
|
actions={
|
||||||
<>
|
<>
|
||||||
{!plugin.details.isDefault && (
|
{!plugin.details.isBundled && (
|
||||||
<ActionButton
|
<ActionButton
|
||||||
id={plugin.id}
|
id={plugin.id}
|
||||||
title="Uninstall plugin"
|
title="Uninstall plugin"
|
||||||
|
|||||||
@@ -10,16 +10,18 @@
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import {
|
import {
|
||||||
PluginDetails,
|
|
||||||
getSourcePlugins,
|
getSourcePlugins,
|
||||||
getInstalledPlugins,
|
getInstalledPlugins,
|
||||||
moveInstalledPluginsFromLegacyDir,
|
moveInstalledPluginsFromLegacyDir,
|
||||||
|
InstalledPluginDetails,
|
||||||
} from 'flipper-plugin-lib';
|
} from 'flipper-plugin-lib';
|
||||||
import {getStaticPath} from '../utils/pathUtils';
|
import {getStaticPath} from '../utils/pathUtils';
|
||||||
|
|
||||||
// Load "dynamic" plugins, e.g. those which are either installed or loaded from sources for development purposes.
|
// Load "dynamic" plugins, e.g. those which are either installed or loaded from sources for development purposes.
|
||||||
// This opposed to "default" plugins which are included into Flipper bundle.
|
// This opposed to "default" plugins which are included into Flipper bundle.
|
||||||
export default async function loadDynamicPlugins(): Promise<PluginDetails[]> {
|
export default async function loadDynamicPlugins(): Promise<
|
||||||
|
InstalledPluginDetails[]
|
||||||
|
> {
|
||||||
if (process.env.FLIPPER_FAST_REFRESH) {
|
if (process.env.FLIPPER_FAST_REFRESH) {
|
||||||
console.log(
|
console.log(
|
||||||
'❌ Skipping loading of dynamic plugins because Fast Refresh is enabled. Fast Refresh only works with bundled plugins.',
|
'❌ Skipping loading of dynamic plugins because Fast Refresh is enabled. Fast Refresh only works with bundled plugins.',
|
||||||
|
|||||||
@@ -39,15 +39,12 @@ export function createMockDownloadablePluginDetails(
|
|||||||
},
|
},
|
||||||
category: 'tools',
|
category: 'tools',
|
||||||
description: 'Description of Test Plugin',
|
description: 'Description of Test Plugin',
|
||||||
dir: `/Users/mock/.flipper/thirdparty/${name}`,
|
|
||||||
entry: `/Users/mock/.flipper/thirdparty/${name}/dist/bundle.js`,
|
|
||||||
flipperSDKVersion: flipperEngineVersion,
|
flipperSDKVersion: flipperEngineVersion,
|
||||||
engines: {
|
engines: {
|
||||||
flipper: flipperEngineVersion,
|
flipper: flipperEngineVersion,
|
||||||
},
|
},
|
||||||
gatekeeper: gatekeeper ?? `GK_${lowercasedID}`,
|
gatekeeper: gatekeeper ?? `GK_${lowercasedID}`,
|
||||||
icon: 'internet',
|
icon: 'internet',
|
||||||
isDefault: false,
|
|
||||||
main: 'dist/bundle.js',
|
main: 'dist/bundle.js',
|
||||||
source: 'src/index.tsx',
|
source: 'src/index.tsx',
|
||||||
specVersion: 2,
|
specVersion: 2,
|
||||||
@@ -55,6 +52,8 @@ export function createMockDownloadablePluginDetails(
|
|||||||
version: version,
|
version: version,
|
||||||
downloadUrl: `http://localhost/${lowercasedID}/${version}`,
|
downloadUrl: `http://localhost/${lowercasedID}/${version}`,
|
||||||
lastUpdated: lastUpdated,
|
lastUpdated: lastUpdated,
|
||||||
|
isBundled: false,
|
||||||
|
isActivatable: false,
|
||||||
};
|
};
|
||||||
return details;
|
return details;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {PluginDetails} from 'flipper-plugin-lib';
|
import {ActivatablePluginDetails} from 'flipper-plugin-lib';
|
||||||
import {PluginFactory, FlipperPluginComponent} from './Plugin';
|
import {PluginFactory, FlipperPluginComponent} from './Plugin';
|
||||||
import {DevicePluginPredicate, DevicePluginFactory} from './DevicePlugin';
|
import {DevicePluginPredicate, DevicePluginFactory} from './DevicePlugin';
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ export type FlipperPluginModule<Factory extends PluginFactory<any, any>> = {
|
|||||||
export class SandyPluginDefinition {
|
export class SandyPluginDefinition {
|
||||||
id: string;
|
id: string;
|
||||||
module: FlipperPluginModule<any> | FlipperDevicePluginModule;
|
module: FlipperPluginModule<any> | FlipperDevicePluginModule;
|
||||||
details: PluginDetails;
|
details: ActivatablePluginDetails;
|
||||||
isDevicePlugin: boolean;
|
isDevicePlugin: boolean;
|
||||||
|
|
||||||
// TODO: Implement T68683476
|
// TODO: Implement T68683476
|
||||||
@@ -58,10 +58,10 @@ export class SandyPluginDefinition {
|
|||||||
| undefined = undefined;
|
| undefined = undefined;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
details: PluginDetails,
|
details: ActivatablePluginDetails,
|
||||||
module: FlipperPluginModule<any> | FlipperDevicePluginModule,
|
module: FlipperPluginModule<any> | FlipperDevicePluginModule,
|
||||||
);
|
);
|
||||||
constructor(details: PluginDetails, module: any) {
|
constructor(details: ActivatablePluginDetails, module: any) {
|
||||||
this.id = details.id;
|
this.id = details.id;
|
||||||
this.details = details;
|
this.details = details;
|
||||||
if (module.supportsDevice) {
|
if (module.supportsDevice) {
|
||||||
@@ -123,8 +123,8 @@ export class SandyPluginDefinition {
|
|||||||
return this.details.version;
|
return this.details.version;
|
||||||
}
|
}
|
||||||
|
|
||||||
get isDefault() {
|
get isBundled() {
|
||||||
return this.details.isDefault;
|
return this.details.isBundled;
|
||||||
}
|
}
|
||||||
|
|
||||||
get keyboardActions() {
|
get keyboardActions() {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
act as testingLibAct,
|
act as testingLibAct,
|
||||||
} from '@testing-library/react';
|
} from '@testing-library/react';
|
||||||
import {queries} from '@testing-library/dom';
|
import {queries} from '@testing-library/dom';
|
||||||
import {PluginDetails} from 'flipper-plugin-lib';
|
import {InstalledPluginDetails} from 'flipper-plugin-lib';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
RealFlipperClient,
|
RealFlipperClient,
|
||||||
@@ -383,15 +383,16 @@ function createBasePluginResult(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function createMockPluginDetails(
|
export function createMockPluginDetails(
|
||||||
details?: Partial<PluginDetails>,
|
details?: Partial<InstalledPluginDetails>,
|
||||||
): PluginDetails {
|
): InstalledPluginDetails {
|
||||||
return {
|
return {
|
||||||
id: 'TestPlugin',
|
id: 'TestPlugin',
|
||||||
dir: '',
|
dir: '',
|
||||||
name: 'TestPlugin',
|
name: 'TestPlugin',
|
||||||
specVersion: 0,
|
specVersion: 0,
|
||||||
entry: '',
|
entry: '',
|
||||||
isDefault: false,
|
isBundled: false,
|
||||||
|
isActivatable: true,
|
||||||
main: '',
|
main: '',
|
||||||
source: '',
|
source: '',
|
||||||
title: 'Testing Plugin',
|
title: 'Testing Plugin',
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import {args} from '@oclif/parser';
|
|||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import {runBuild} from 'flipper-pkg-lib';
|
import {runBuild} from 'flipper-pkg-lib';
|
||||||
import {getPluginDetailsFromDir} from 'flipper-plugin-lib';
|
import {getInstalledPluginDetails} from 'flipper-plugin-lib';
|
||||||
|
|
||||||
export default class Bundle extends Command {
|
export default class Bundle extends Command {
|
||||||
public static description = 'transpiles and bundles plugin';
|
public static description = 'transpiles and bundles plugin';
|
||||||
@@ -55,7 +55,7 @@ export default class Bundle extends Command {
|
|||||||
`package.json is not found in plugin source directory ${inputDirectory}.`,
|
`package.json is not found in plugin source directory ${inputDirectory}.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const plugin = await getPluginDetailsFromDir(inputDirectory);
|
const plugin = await getInstalledPluginDetails(inputDirectory);
|
||||||
const out = path.resolve(inputDirectory, plugin.main);
|
const out = path.resolve(inputDirectory, plugin.main);
|
||||||
await fs.ensureDir(path.dirname(out));
|
await fs.ensureDir(path.dirname(out));
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import * as path from 'path';
|
|||||||
import * as yarn from '../utils/yarn';
|
import * as yarn from '../utils/yarn';
|
||||||
import cli from 'cli-ux';
|
import cli from 'cli-ux';
|
||||||
import {runBuild} from 'flipper-pkg-lib';
|
import {runBuild} from 'flipper-pkg-lib';
|
||||||
import {getPluginDetailsFromDir} from 'flipper-plugin-lib';
|
import {getInstalledPluginDetails} from 'flipper-plugin-lib';
|
||||||
|
|
||||||
async function deriveOutputFileName(inputDirectory: string): Promise<string> {
|
async function deriveOutputFileName(inputDirectory: string): Promise<string> {
|
||||||
const packageJson = await readJSON(path.join(inputDirectory, 'package.json'));
|
const packageJson = await readJSON(path.join(inputDirectory, 'package.json'));
|
||||||
@@ -116,7 +116,7 @@ export default class Pack extends Command {
|
|||||||
cli.action.stop();
|
cli.action.stop();
|
||||||
|
|
||||||
cli.action.start('Reading plugin details');
|
cli.action.start('Reading plugin details');
|
||||||
const plugin = await getPluginDetailsFromDir(inputDirectory);
|
const plugin = await getInstalledPluginDetails(inputDirectory);
|
||||||
const out = path.resolve(inputDirectory, plugin.main);
|
const out = path.resolve(inputDirectory, plugin.main);
|
||||||
cli.action.stop(`done. Source: ${plugin.source}. Main: ${plugin.main}.`);
|
cli.action.stop(`done. Source: ${plugin.source}. Main: ${plugin.main}.`);
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import {getPluginDetails} from 'flipper-plugin-lib';
|
import {getInstalledPluginDetails} from 'flipper-plugin-lib';
|
||||||
import {kebabCase} from 'lodash';
|
import {kebabCase} from 'lodash';
|
||||||
|
|
||||||
export default async function (
|
export default async function (
|
||||||
@@ -36,7 +36,7 @@ export default async function (
|
|||||||
console.log(`⚙️ Migrating Flipper plugin package in ${dir}`);
|
console.log(`⚙️ Migrating Flipper plugin package in ${dir}`);
|
||||||
const packageJsonString = (await fs.readFile(packageJsonPath)).toString();
|
const packageJsonString = (await fs.readFile(packageJsonPath)).toString();
|
||||||
const packageJson = JSON.parse(packageJsonString);
|
const packageJson = JSON.parse(packageJsonString);
|
||||||
const pluginDetails = await getPluginDetails(dir, packageJson);
|
const pluginDetails = await getInstalledPluginDetails(dir, packageJson);
|
||||||
if (pluginDetails.specVersion === 2) {
|
if (pluginDetails.specVersion === 2) {
|
||||||
console.log(
|
console.log(
|
||||||
`✅ Plugin is already defined according to the latest specification version.`,
|
`✅ Plugin is already defined according to the latest specification version.`,
|
||||||
|
|||||||
@@ -8,15 +8,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export interface PluginDetails {
|
export interface PluginDetails {
|
||||||
dir: string;
|
|
||||||
name: string;
|
name: string;
|
||||||
specVersion: number;
|
specVersion: number;
|
||||||
version: string;
|
version: string;
|
||||||
source: string;
|
source: string;
|
||||||
main: string;
|
main: string;
|
||||||
id: string;
|
id: string;
|
||||||
isDefault: boolean;
|
|
||||||
entry: string;
|
|
||||||
gatekeeper?: string;
|
gatekeeper?: string;
|
||||||
title: string;
|
title: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
@@ -32,7 +29,36 @@ export interface PluginDetails {
|
|||||||
flipperSDKVersion?: string;
|
flipperSDKVersion?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DownloadablePluginDetails extends PluginDetails {
|
export interface ConcretePluginDetails extends PluginDetails {
|
||||||
|
// Determines whether the plugin is a part of the Flipper JS bundle.
|
||||||
|
isBundled: boolean;
|
||||||
|
// Determines whether the plugin is physically available for activation in Flipper.
|
||||||
|
isActivatable: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describes plugin which is a part of the Flipper JS bundle.
|
||||||
|
export interface BundledPluginDetails extends ConcretePluginDetails {
|
||||||
|
isBundled: true;
|
||||||
|
isActivatable: true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describes plugin installed on the disk.
|
||||||
|
export interface InstalledPluginDetails extends ConcretePluginDetails {
|
||||||
|
isBundled: false;
|
||||||
|
isActivatable: true;
|
||||||
|
dir: string;
|
||||||
|
entry: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Describes plugin physically available for activation in Flipper.
|
||||||
|
export type ActivatablePluginDetails =
|
||||||
|
| BundledPluginDetails
|
||||||
|
| InstalledPluginDetails;
|
||||||
|
|
||||||
|
// Describes plugin available for downloading. Until downloaded to the disk it is not available for activation in Flipper.
|
||||||
|
export interface DownloadablePluginDetails extends ConcretePluginDetails {
|
||||||
|
isActivatable: false;
|
||||||
|
isBundled: false;
|
||||||
downloadUrl: string;
|
downloadUrl: string;
|
||||||
lastUpdated: Date;
|
lastUpdated: Date;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import {getPluginDetailsFromDir} from '../getPluginDetails';
|
import {getInstalledPluginDetails} from '../getPluginDetails';
|
||||||
import {pluginInstallationDir} from '../pluginPaths';
|
import {pluginInstallationDir} from '../pluginPaths';
|
||||||
import {normalizePath} from 'flipper-test-utils';
|
import {normalizePath} from 'flipper-test-utils';
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ test('getPluginDetailsV1', async () => {
|
|||||||
};
|
};
|
||||||
jest.mock('fs-extra', () => jest.fn());
|
jest.mock('fs-extra', () => jest.fn());
|
||||||
fs.readJson = jest.fn().mockImplementation(() => pluginV1);
|
fs.readJson = jest.fn().mockImplementation(() => pluginV1);
|
||||||
const details = await getPluginDetailsFromDir(pluginPath);
|
const details = await getInstalledPluginDetails(pluginPath);
|
||||||
details.dir = normalizePath(details.dir);
|
details.dir = normalizePath(details.dir);
|
||||||
details.entry = normalizePath(details.entry);
|
details.entry = normalizePath(details.entry);
|
||||||
expect(details).toMatchInlineSnapshot(`
|
expect(details).toMatchInlineSnapshot(`
|
||||||
@@ -45,7 +45,8 @@ test('getPluginDetailsV1', async () => {
|
|||||||
"gatekeeper": "GK_flipper_plugin_test",
|
"gatekeeper": "GK_flipper_plugin_test",
|
||||||
"icon": undefined,
|
"icon": undefined,
|
||||||
"id": "flipper-plugin-test",
|
"id": "flipper-plugin-test",
|
||||||
"isDefault": false,
|
"isActivatable": true,
|
||||||
|
"isBundled": false,
|
||||||
"main": "dist/bundle.js",
|
"main": "dist/bundle.js",
|
||||||
"name": "flipper-plugin-test",
|
"name": "flipper-plugin-test",
|
||||||
"source": "src/index.tsx",
|
"source": "src/index.tsx",
|
||||||
@@ -69,7 +70,7 @@ test('getPluginDetailsV2', async () => {
|
|||||||
};
|
};
|
||||||
jest.mock('fs-extra', () => jest.fn());
|
jest.mock('fs-extra', () => jest.fn());
|
||||||
fs.readJson = jest.fn().mockImplementation(() => pluginV2);
|
fs.readJson = jest.fn().mockImplementation(() => pluginV2);
|
||||||
const details = await getPluginDetailsFromDir(pluginPath);
|
const details = await getInstalledPluginDetails(pluginPath);
|
||||||
details.dir = normalizePath(details.dir);
|
details.dir = normalizePath(details.dir);
|
||||||
details.entry = normalizePath(details.entry);
|
details.entry = normalizePath(details.entry);
|
||||||
expect(details).toMatchInlineSnapshot(`
|
expect(details).toMatchInlineSnapshot(`
|
||||||
@@ -83,7 +84,8 @@ test('getPluginDetailsV2', async () => {
|
|||||||
"gatekeeper": "GK_flipper_plugin_test",
|
"gatekeeper": "GK_flipper_plugin_test",
|
||||||
"icon": undefined,
|
"icon": undefined,
|
||||||
"id": "flipper-plugin-test",
|
"id": "flipper-plugin-test",
|
||||||
"isDefault": false,
|
"isActivatable": true,
|
||||||
|
"isBundled": false,
|
||||||
"main": "dist/bundle.js",
|
"main": "dist/bundle.js",
|
||||||
"name": "flipper-plugin-test",
|
"name": "flipper-plugin-test",
|
||||||
"source": "src/index.tsx",
|
"source": "src/index.tsx",
|
||||||
@@ -107,7 +109,7 @@ test('id used as title if the latter omited', async () => {
|
|||||||
};
|
};
|
||||||
jest.mock('fs-extra', () => jest.fn());
|
jest.mock('fs-extra', () => jest.fn());
|
||||||
fs.readJson = jest.fn().mockImplementation(() => pluginV2);
|
fs.readJson = jest.fn().mockImplementation(() => pluginV2);
|
||||||
const details = await getPluginDetailsFromDir(pluginPath);
|
const details = await getInstalledPluginDetails(pluginPath);
|
||||||
details.dir = normalizePath(details.dir);
|
details.dir = normalizePath(details.dir);
|
||||||
details.entry = normalizePath(details.entry);
|
details.entry = normalizePath(details.entry);
|
||||||
expect(details).toMatchInlineSnapshot(`
|
expect(details).toMatchInlineSnapshot(`
|
||||||
@@ -121,7 +123,8 @@ test('id used as title if the latter omited', async () => {
|
|||||||
"gatekeeper": "GK_flipper_plugin_test",
|
"gatekeeper": "GK_flipper_plugin_test",
|
||||||
"icon": undefined,
|
"icon": undefined,
|
||||||
"id": "test",
|
"id": "test",
|
||||||
"isDefault": false,
|
"isActivatable": true,
|
||||||
|
"isBundled": false,
|
||||||
"main": "dist/bundle.js",
|
"main": "dist/bundle.js",
|
||||||
"name": "flipper-plugin-test",
|
"name": "flipper-plugin-test",
|
||||||
"source": "src/index.tsx",
|
"source": "src/index.tsx",
|
||||||
@@ -144,7 +147,7 @@ test('name without "flipper-plugin-" prefix is used as title if the latter omite
|
|||||||
};
|
};
|
||||||
jest.mock('fs-extra', () => jest.fn());
|
jest.mock('fs-extra', () => jest.fn());
|
||||||
fs.readJson = jest.fn().mockImplementation(() => pluginV2);
|
fs.readJson = jest.fn().mockImplementation(() => pluginV2);
|
||||||
const details = await getPluginDetailsFromDir(pluginPath);
|
const details = await getInstalledPluginDetails(pluginPath);
|
||||||
details.dir = normalizePath(details.dir);
|
details.dir = normalizePath(details.dir);
|
||||||
details.entry = normalizePath(details.entry);
|
details.entry = normalizePath(details.entry);
|
||||||
expect(details).toMatchInlineSnapshot(`
|
expect(details).toMatchInlineSnapshot(`
|
||||||
@@ -158,7 +161,8 @@ test('name without "flipper-plugin-" prefix is used as title if the latter omite
|
|||||||
"gatekeeper": "GK_flipper_plugin_test",
|
"gatekeeper": "GK_flipper_plugin_test",
|
||||||
"icon": undefined,
|
"icon": undefined,
|
||||||
"id": "flipper-plugin-test",
|
"id": "flipper-plugin-test",
|
||||||
"isDefault": false,
|
"isActivatable": true,
|
||||||
|
"isBundled": false,
|
||||||
"main": "dist/bundle.js",
|
"main": "dist/bundle.js",
|
||||||
"name": "flipper-plugin-test",
|
"name": "flipper-plugin-test",
|
||||||
"source": "src/index.tsx",
|
"source": "src/index.tsx",
|
||||||
@@ -184,7 +188,7 @@ test('flipper-plugin-version is parsed', async () => {
|
|||||||
};
|
};
|
||||||
jest.mock('fs-extra', () => jest.fn());
|
jest.mock('fs-extra', () => jest.fn());
|
||||||
fs.readJson = jest.fn().mockImplementation(() => pluginV2);
|
fs.readJson = jest.fn().mockImplementation(() => pluginV2);
|
||||||
const details = await getPluginDetailsFromDir(pluginPath);
|
const details = await getInstalledPluginDetails(pluginPath);
|
||||||
details.dir = normalizePath(details.dir);
|
details.dir = normalizePath(details.dir);
|
||||||
details.entry = normalizePath(details.entry);
|
details.entry = normalizePath(details.entry);
|
||||||
expect(details).toMatchInlineSnapshot(`
|
expect(details).toMatchInlineSnapshot(`
|
||||||
@@ -198,7 +202,8 @@ test('flipper-plugin-version is parsed', async () => {
|
|||||||
"gatekeeper": "GK_flipper_plugin_test",
|
"gatekeeper": "GK_flipper_plugin_test",
|
||||||
"icon": undefined,
|
"icon": undefined,
|
||||||
"id": "flipper-plugin-test",
|
"id": "flipper-plugin-test",
|
||||||
"isDefault": false,
|
"isActivatable": true,
|
||||||
|
"isBundled": false,
|
||||||
"main": "dist/bundle.js",
|
"main": "dist/bundle.js",
|
||||||
"name": "flipper-plugin-test",
|
"name": "flipper-plugin-test",
|
||||||
"source": "src/index.tsx",
|
"source": "src/index.tsx",
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import {
|
|||||||
import {getInstalledPlugins} from '../pluginInstaller';
|
import {getInstalledPlugins} from '../pluginInstaller';
|
||||||
import {mocked} from 'ts-jest/utils';
|
import {mocked} from 'ts-jest/utils';
|
||||||
import type {Package} from 'npm-api';
|
import type {Package} from 'npm-api';
|
||||||
import PluginDetails from '../PluginDetails';
|
import {InstalledPluginDetails} from '../PluginDetails';
|
||||||
|
|
||||||
jest.mock('npm-api', () => {
|
jest.mock('npm-api', () => {
|
||||||
return jest.fn().mockImplementation(() => {
|
return jest.fn().mockImplementation(() => {
|
||||||
@@ -54,7 +54,7 @@ jest.mock('npm-api', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const installedPlugins: PluginDetails[] = [
|
const installedPlugins: InstalledPluginDetails[] = [
|
||||||
{
|
{
|
||||||
name: 'flipper-plugin-hello',
|
name: 'flipper-plugin-hello',
|
||||||
entry: './test/index.js',
|
entry: './test/index.js',
|
||||||
@@ -66,7 +66,8 @@ const installedPlugins: PluginDetails[] = [
|
|||||||
id: 'Hello',
|
id: 'Hello',
|
||||||
title: 'Hello',
|
title: 'Hello',
|
||||||
description: 'World?',
|
description: 'World?',
|
||||||
isDefault: false,
|
isBundled: false,
|
||||||
|
isActivatable: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'flipper-plugin-world',
|
name: 'flipper-plugin-world',
|
||||||
@@ -79,7 +80,8 @@ const installedPlugins: PluginDetails[] = [
|
|||||||
id: 'World',
|
id: 'World',
|
||||||
title: 'World',
|
title: 'World',
|
||||||
description: 'Hello?',
|
description: 'Hello?',
|
||||||
isDefault: false,
|
isBundled: false,
|
||||||
|
isActivatable: true,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -9,10 +9,14 @@
|
|||||||
|
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import {PluginDetails} from './PluginDetails';
|
import {
|
||||||
import {getPluginVersionInstallationDir, pluginCacheDir} from './pluginPaths';
|
DownloadablePluginDetails,
|
||||||
|
InstalledPluginDetails,
|
||||||
|
PluginDetails,
|
||||||
|
} from './PluginDetails';
|
||||||
|
import {pluginCacheDir} from './pluginPaths';
|
||||||
|
|
||||||
export async function getPluginDetails(pluginDir: string, packageJson: any) {
|
export function getPluginDetails(packageJson: any): PluginDetails {
|
||||||
const specVersion =
|
const specVersion =
|
||||||
packageJson.$schema &&
|
packageJson.$schema &&
|
||||||
packageJson.$schema ===
|
packageJson.$schema ===
|
||||||
@@ -21,60 +25,61 @@ export async function getPluginDetails(pluginDir: string, packageJson: any) {
|
|||||||
: 1;
|
: 1;
|
||||||
switch (specVersion) {
|
switch (specVersion) {
|
||||||
case 1:
|
case 1:
|
||||||
return await getPluginDetailsV1(pluginDir, packageJson);
|
return getPluginDetailsV1(packageJson);
|
||||||
case 2:
|
case 2:
|
||||||
return await getPluginDetailsV2(pluginDir, packageJson);
|
return getPluginDetailsV2(packageJson);
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown plugin format version: ${specVersion}`);
|
throw new Error(`Unknown plugin format version: ${specVersion}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getPluginDetailsFromDir(
|
export async function getInstalledPluginDetails(
|
||||||
pluginDir: string,
|
dir: string,
|
||||||
): Promise<PluginDetails> {
|
packageJson?: any,
|
||||||
const packageJson = await fs.readJson(path.join(pluginDir, 'package.json'));
|
): Promise<InstalledPluginDetails> {
|
||||||
return await getPluginDetails(pluginDir, packageJson);
|
packageJson =
|
||||||
|
packageJson ?? (await fs.readJson(path.join(dir, 'package.json')));
|
||||||
|
const pluginDetails = getPluginDetails(packageJson);
|
||||||
|
const entry =
|
||||||
|
pluginDetails.specVersion === 1
|
||||||
|
? path.resolve(
|
||||||
|
pluginCacheDir,
|
||||||
|
`${packageJson.name}@${packageJson.version || '0.0.0'}.js`,
|
||||||
|
)
|
||||||
|
: path.resolve(dir, packageJson.main);
|
||||||
|
return {
|
||||||
|
...pluginDetails,
|
||||||
|
isBundled: false,
|
||||||
|
isActivatable: true,
|
||||||
|
dir,
|
||||||
|
entry,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getPluginDetailsFromPackageJson(packageJson: any) {
|
export function getDownloadablePluginDetails(
|
||||||
const pluginDir = getPluginVersionInstallationDir(
|
|
||||||
packageJson.name,
|
|
||||||
packageJson.version,
|
|
||||||
);
|
|
||||||
return await getPluginDetails(pluginDir, packageJson);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getDownloadablePluginDetails(
|
|
||||||
packageJson: any,
|
packageJson: any,
|
||||||
downloadUrl: string,
|
downloadUrl: string,
|
||||||
lastUpdated: Date,
|
lastUpdated: Date,
|
||||||
) {
|
): DownloadablePluginDetails {
|
||||||
const details = await getPluginDetailsFromPackageJson(packageJson);
|
const details = getPluginDetails(packageJson);
|
||||||
return {
|
return {
|
||||||
...details,
|
...details,
|
||||||
|
isBundled: false,
|
||||||
|
isActivatable: false,
|
||||||
downloadUrl,
|
downloadUrl,
|
||||||
lastUpdated,
|
lastUpdated,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Plugins packaged using V1 are distributed as sources and compiled in run-time.
|
// Plugins packaged using V1 are distributed as sources and compiled in run-time.
|
||||||
async function getPluginDetailsV1(
|
function getPluginDetailsV1(packageJson: any): PluginDetails {
|
||||||
pluginDir: string,
|
|
||||||
packageJson: any,
|
|
||||||
): Promise<PluginDetails> {
|
|
||||||
return {
|
return {
|
||||||
specVersion: 1,
|
specVersion: 1,
|
||||||
dir: pluginDir,
|
|
||||||
name: packageJson.name,
|
name: packageJson.name,
|
||||||
version: packageJson.version,
|
version: packageJson.version,
|
||||||
main: 'dist/bundle.js',
|
main: 'dist/bundle.js',
|
||||||
entry: path.join(
|
|
||||||
pluginCacheDir,
|
|
||||||
`${packageJson.name}@${packageJson.version || '0.0.0'}.js`,
|
|
||||||
),
|
|
||||||
source: packageJson.main,
|
source: packageJson.main,
|
||||||
id: packageJson.name,
|
id: packageJson.name,
|
||||||
isDefault: false,
|
|
||||||
gatekeeper: packageJson.gatekeeper,
|
gatekeeper: packageJson.gatekeeper,
|
||||||
icon: packageJson.icon,
|
icon: packageJson.icon,
|
||||||
title: packageJson.title || packageJson.name,
|
title: packageJson.title || packageJson.name,
|
||||||
@@ -86,19 +91,13 @@ async function getPluginDetailsV1(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Plugins packaged using V2 are pre-bundled, so compilation in run-time is not required for them.
|
// Plugins packaged using V2 are pre-bundled, so compilation in run-time is not required for them.
|
||||||
async function getPluginDetailsV2(
|
function getPluginDetailsV2(packageJson: any): PluginDetails {
|
||||||
pluginDir: string,
|
|
||||||
packageJson: any,
|
|
||||||
): Promise<PluginDetails> {
|
|
||||||
return {
|
return {
|
||||||
specVersion: 2,
|
specVersion: 2,
|
||||||
dir: pluginDir,
|
|
||||||
name: packageJson.name,
|
name: packageJson.name,
|
||||||
version: packageJson.version,
|
version: packageJson.version,
|
||||||
main: packageJson.main,
|
main: packageJson.main,
|
||||||
entry: path.resolve(pluginDir, packageJson.main),
|
|
||||||
source: packageJson.flipperBundlerEntry,
|
source: packageJson.flipperBundlerEntry,
|
||||||
isDefault: false,
|
|
||||||
id: packageJson.id || packageJson.name,
|
id: packageJson.id || packageJson.name,
|
||||||
gatekeeper: packageJson.gatekeeper,
|
gatekeeper: packageJson.gatekeeper,
|
||||||
icon: packageJson.icon,
|
icon: packageJson.icon,
|
||||||
@@ -111,9 +110,10 @@ async function getPluginDetailsV2(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTitleFromName(name: string) {
|
function getTitleFromName(name: string): string {
|
||||||
const prefix = 'flipper-plugin-';
|
const prefix = 'flipper-plugin-';
|
||||||
if (name.startsWith(prefix)) {
|
if (name.startsWith(prefix)) {
|
||||||
return name.substr(prefix.length);
|
return name.substr(prefix.length);
|
||||||
}
|
}
|
||||||
|
return name;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,16 +11,17 @@ import path from 'path';
|
|||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import expandTilde from 'expand-tilde';
|
import expandTilde from 'expand-tilde';
|
||||||
import {getPluginSourceFolders} from './pluginPaths';
|
import {getPluginSourceFolders} from './pluginPaths';
|
||||||
import {PluginDetails, getPluginDetails} from 'flipper-plugin-lib';
|
|
||||||
import pmap from 'p-map';
|
import pmap from 'p-map';
|
||||||
import pfilter from 'p-filter';
|
import pfilter from 'p-filter';
|
||||||
import {satisfies} from 'semver';
|
import {satisfies} from 'semver';
|
||||||
|
import {getInstalledPluginDetails} from './getPluginDetails';
|
||||||
|
import {InstalledPluginDetails} from './PluginDetails';
|
||||||
|
|
||||||
const flipperVersion = require('../package.json').version;
|
const flipperVersion = require('../package.json').version;
|
||||||
|
|
||||||
export async function getSourcePlugins(): Promise<PluginDetails[]> {
|
export async function getSourcePlugins(): Promise<InstalledPluginDetails[]> {
|
||||||
const pluginFolders = await getPluginSourceFolders();
|
const pluginFolders = await getPluginSourceFolders();
|
||||||
const entryPoints: {[key: string]: PluginDetails} = {};
|
const entryPoints: {[key: string]: InstalledPluginDetails} = {};
|
||||||
const additionalPlugins = await pmap(pluginFolders, (path) =>
|
const additionalPlugins = await pmap(pluginFolders, (path) =>
|
||||||
entryPointForPluginFolder(path),
|
entryPointForPluginFolder(path),
|
||||||
);
|
);
|
||||||
@@ -47,7 +48,7 @@ export async function getSourcePlugins(): Promise<PluginDetails[]> {
|
|||||||
}
|
}
|
||||||
async function entryPointForPluginFolder(
|
async function entryPointForPluginFolder(
|
||||||
pluginsDir: string,
|
pluginsDir: string,
|
||||||
): Promise<{[key: string]: PluginDetails}> {
|
): Promise<{[key: string]: InstalledPluginDetails}> {
|
||||||
pluginsDir = expandTilde(pluginsDir);
|
pluginsDir = expandTilde(pluginsDir);
|
||||||
if (!fs.existsSync(pluginsDir)) {
|
if (!fs.existsSync(pluginsDir)) {
|
||||||
return {};
|
return {};
|
||||||
@@ -96,7 +97,7 @@ async function entryPointForPluginFolder(
|
|||||||
.then((packages) =>
|
.then((packages) =>
|
||||||
pmap(packages, async ({manifest, dir}) => {
|
pmap(packages, async ({manifest, dir}) => {
|
||||||
try {
|
try {
|
||||||
const details = await getPluginDetails(dir, manifest);
|
const details = await getInstalledPluginDetails(dir, manifest);
|
||||||
if (
|
if (
|
||||||
details.flipperSDKVersion &&
|
details.flipperSDKVersion &&
|
||||||
!satisfies(flipperVersion, details.flipperSDKVersion)
|
!satisfies(flipperVersion, details.flipperSDKVersion)
|
||||||
@@ -117,7 +118,7 @@ async function entryPointForPluginFolder(
|
|||||||
)
|
)
|
||||||
.then((plugins) => plugins.filter(notNull))
|
.then((plugins) => plugins.filter(notNull))
|
||||||
.then((plugins) =>
|
.then((plugins) =>
|
||||||
plugins.reduce<{[key: string]: PluginDetails}>((acc, cv) => {
|
plugins.reduce<{[key: string]: InstalledPluginDetails}>((acc, cv) => {
|
||||||
acc[cv!.name] = cv!;
|
acc[cv!.name] = cv!;
|
||||||
return acc;
|
return acc;
|
||||||
}, {}),
|
}, {}),
|
||||||
|
|||||||
@@ -7,15 +7,12 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import PluginDetails from './PluginDetails';
|
import {InstalledPluginDetails} from './PluginDetails';
|
||||||
import {getInstalledPlugins} from './pluginInstaller';
|
import {getInstalledPlugins} from './pluginInstaller';
|
||||||
import semver from 'semver';
|
import semver from 'semver';
|
||||||
import {getNpmHostedPlugins, NpmPackageDescriptor} from './getNpmHostedPlugins';
|
import {getNpmHostedPlugins, NpmPackageDescriptor} from './getNpmHostedPlugins';
|
||||||
import NpmApi from 'npm-api';
|
import NpmApi from 'npm-api';
|
||||||
import {
|
import {getInstalledPluginDetails, getPluginDetails} from './getPluginDetails';
|
||||||
getPluginDetails,
|
|
||||||
getPluginDetailsFromPackageJson,
|
|
||||||
} from './getPluginDetails';
|
|
||||||
import {getPluginVersionInstallationDir} from './pluginPaths';
|
import {getPluginVersionInstallationDir} from './pluginPaths';
|
||||||
import pmap from 'p-map';
|
import pmap from 'p-map';
|
||||||
import {notNull} from './typeUtils';
|
import {notNull} from './typeUtils';
|
||||||
@@ -31,7 +28,7 @@ export type UpdatablePlugin = {
|
|||||||
updateStatus: UpdateResult;
|
updateStatus: UpdateResult;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UpdatablePluginDetails = PluginDetails & UpdatablePlugin;
|
export type UpdatablePluginDetails = InstalledPluginDetails & UpdatablePlugin;
|
||||||
|
|
||||||
export async function getUpdatablePlugins(
|
export async function getUpdatablePlugins(
|
||||||
query?: string,
|
query?: string,
|
||||||
@@ -51,7 +48,7 @@ export async function getUpdatablePlugins(
|
|||||||
semver.lt(installedPlugin.version, npmPackageDescriptor.version)
|
semver.lt(installedPlugin.version, npmPackageDescriptor.version)
|
||||||
) {
|
) {
|
||||||
const pkg = await npmApi.repo(npmPackageDescriptor.name).package();
|
const pkg = await npmApi.repo(npmPackageDescriptor.name).package();
|
||||||
const npmPluginDetails = await getPluginDetails(
|
const npmPluginDetails = await getInstalledPluginDetails(
|
||||||
getPluginVersionInstallationDir(
|
getPluginVersionInstallationDir(
|
||||||
npmPackageDescriptor.name,
|
npmPackageDescriptor.name,
|
||||||
npmPackageDescriptor.version,
|
npmPackageDescriptor.version,
|
||||||
@@ -91,7 +88,7 @@ export async function getUpdatablePlugins(
|
|||||||
async (notInstalledPlugin) => {
|
async (notInstalledPlugin) => {
|
||||||
try {
|
try {
|
||||||
const pkg = await npmApi.repo(notInstalledPlugin.name).package();
|
const pkg = await npmApi.repo(notInstalledPlugin.name).package();
|
||||||
const npmPluginDetails = await getPluginDetailsFromPackageJson(pkg);
|
const npmPluginDetails = getPluginDetails(pkg);
|
||||||
if (npmPluginDetails.specVersion === 1) {
|
if (npmPluginDetails.specVersion === 1) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ import decompress from 'decompress';
|
|||||||
import decompressTargz from 'decompress-targz';
|
import decompressTargz from 'decompress-targz';
|
||||||
import decompressUnzip from 'decompress-unzip';
|
import decompressUnzip from 'decompress-unzip';
|
||||||
import tmp from 'tmp';
|
import tmp from 'tmp';
|
||||||
import PluginDetails from './PluginDetails';
|
import {InstalledPluginDetails} from './PluginDetails';
|
||||||
import {getPluginDetailsFromDir} from './getPluginDetails';
|
import {getInstalledPluginDetails} from './getPluginDetails';
|
||||||
import {
|
import {
|
||||||
getPluginVersionInstallationDir,
|
getPluginVersionInstallationDir,
|
||||||
getPluginDirNameFromPackageName,
|
getPluginDirNameFromPackageName,
|
||||||
@@ -37,8 +37,8 @@ function providePluginManagerNoDependencies(): PM {
|
|||||||
|
|
||||||
async function installPluginFromTempDir(
|
async function installPluginFromTempDir(
|
||||||
sourceDir: string,
|
sourceDir: string,
|
||||||
): Promise<PluginDetails> {
|
): Promise<InstalledPluginDetails> {
|
||||||
const pluginDetails = await getPluginDetailsFromDir(sourceDir);
|
const pluginDetails = await getInstalledPluginDetails(sourceDir);
|
||||||
const {name, version} = pluginDetails;
|
const {name, version} = pluginDetails;
|
||||||
const backupDir = path.join(await getTmpDir(), `${name}-${version}`);
|
const backupDir = path.join(await getTmpDir(), `${name}-${version}`);
|
||||||
const destinationDir = getPluginVersionInstallationDir(name, version);
|
const destinationDir = getPluginVersionInstallationDir(name, version);
|
||||||
@@ -63,7 +63,7 @@ async function installPluginFromTempDir(
|
|||||||
}
|
}
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
return await getPluginDetailsFromDir(destinationDir);
|
return await getInstalledPluginDetails(destinationDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getPluginRootDir(dir: string) {
|
async function getPluginRootDir(dir: string) {
|
||||||
@@ -87,12 +87,12 @@ async function getPluginRootDir(dir: string) {
|
|||||||
export async function getInstalledPlugin(
|
export async function getInstalledPlugin(
|
||||||
name: string,
|
name: string,
|
||||||
version: string,
|
version: string,
|
||||||
): Promise<PluginDetails | null> {
|
): Promise<InstalledPluginDetails | null> {
|
||||||
const dir = getPluginVersionInstallationDir(name, version);
|
const dir = getPluginVersionInstallationDir(name, version);
|
||||||
if (!(await fs.pathExists(dir))) {
|
if (!(await fs.pathExists(dir))) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return await getPluginDetailsFromDir(dir);
|
return await getInstalledPluginDetails(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function installPluginFromNpm(name: string) {
|
export async function installPluginFromNpm(name: string) {
|
||||||
@@ -114,7 +114,7 @@ export async function installPluginFromNpm(name: string) {
|
|||||||
|
|
||||||
export async function installPluginFromFile(
|
export async function installPluginFromFile(
|
||||||
packagePath: string,
|
packagePath: string,
|
||||||
): Promise<PluginDetails> {
|
): Promise<InstalledPluginDetails> {
|
||||||
const tmpDir = await getTmpDir();
|
const tmpDir = await getTmpDir();
|
||||||
try {
|
try {
|
||||||
const files = await decompress(packagePath, tmpDir, {
|
const files = await decompress(packagePath, tmpDir, {
|
||||||
@@ -140,14 +140,14 @@ export async function removePlugins(
|
|||||||
await pmap(names, (name) => removePlugin(name));
|
await pmap(names, (name) => removePlugin(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getInstalledPlugins(): Promise<PluginDetails[]> {
|
export async function getInstalledPlugins(): Promise<InstalledPluginDetails[]> {
|
||||||
const versionDirs = await getInstalledPluginVersionDirs();
|
const versionDirs = await getInstalledPluginVersionDirs();
|
||||||
return pmap(
|
return pmap(
|
||||||
versionDirs
|
versionDirs
|
||||||
.filter(([_, versionDirs]) => versionDirs.length > 0)
|
.filter(([_, versionDirs]) => versionDirs.length > 0)
|
||||||
.map(([_, versionDirs]) => versionDirs[0]),
|
.map(([_, versionDirs]) => versionDirs[0]),
|
||||||
(latestVersionDir) =>
|
(latestVersionDir) =>
|
||||||
getPluginDetailsFromDir(latestVersionDir).catch((err) => {
|
getInstalledPluginDetails(latestVersionDir).catch((err) => {
|
||||||
console.error(`Failed to load plugin from ${latestVersionDir}`, err);
|
console.error(`Failed to load plugin from ${latestVersionDir}`, err);
|
||||||
return null;
|
return null;
|
||||||
}),
|
}),
|
||||||
@@ -187,7 +187,7 @@ export async function moveInstalledPluginsFromLegacyDir() {
|
|||||||
)
|
)
|
||||||
.then((dirs) =>
|
.then((dirs) =>
|
||||||
pmap(dirs, (dir) =>
|
pmap(dirs, (dir) =>
|
||||||
getPluginDetailsFromDir(dir).catch(async (err) => {
|
getInstalledPluginDetails(dir).catch(async (err) => {
|
||||||
console.error(
|
console.error(
|
||||||
`Failed to load plugin from ${dir} on moving legacy plugins. Removing it.`,
|
`Failed to load plugin from ${dir} on moving legacy plugins. Removing it.`,
|
||||||
err,
|
err,
|
||||||
|
|||||||
@@ -14,7 +14,11 @@ import fs from 'fs-extra';
|
|||||||
import {spawn} from 'promisify-child-process';
|
import {spawn} from 'promisify-child-process';
|
||||||
import {getWatchFolders} from 'flipper-pkg-lib';
|
import {getWatchFolders} from 'flipper-pkg-lib';
|
||||||
import getAppWatchFolders from './get-app-watch-folders';
|
import getAppWatchFolders from './get-app-watch-folders';
|
||||||
import {getSourcePlugins, getPluginSourceFolders} from 'flipper-plugin-lib';
|
import {
|
||||||
|
getSourcePlugins,
|
||||||
|
getPluginSourceFolders,
|
||||||
|
BundledPluginDetails,
|
||||||
|
} from 'flipper-plugin-lib';
|
||||||
import {
|
import {
|
||||||
appDir,
|
appDir,
|
||||||
staticDir,
|
staticDir,
|
||||||
@@ -33,19 +37,26 @@ export function die(err: Error) {
|
|||||||
|
|
||||||
export async function generatePluginEntryPoints() {
|
export async function generatePluginEntryPoints() {
|
||||||
console.log('⚙️ Generating plugin entry points...');
|
console.log('⚙️ Generating plugin entry points...');
|
||||||
const plugins = await getSourcePlugins();
|
const sourcePlugins = await getSourcePlugins();
|
||||||
for (const plugin of plugins) {
|
const bundledPlugins = sourcePlugins.map(
|
||||||
plugin.isDefault = true;
|
(p) =>
|
||||||
plugin.version = plugin.version === '0.0.0' ? version : plugin.version;
|
({
|
||||||
plugin.flipperSDKVersion =
|
...p,
|
||||||
plugin.flipperSDKVersion === '0.0.0' ? version : plugin.flipperSDKVersion;
|
isBundled: true,
|
||||||
}
|
version: p.version === '0.0.0' ? version : p.version,
|
||||||
|
flipperSDKVersion:
|
||||||
|
p.flipperSDKVersion === '0.0.0' ? version : p.flipperSDKVersion,
|
||||||
|
} as BundledPluginDetails),
|
||||||
|
);
|
||||||
if (await fs.pathExists(defaultPluginsIndexDir)) {
|
if (await fs.pathExists(defaultPluginsIndexDir)) {
|
||||||
await fs.remove(defaultPluginsIndexDir);
|
await fs.remove(defaultPluginsIndexDir);
|
||||||
}
|
}
|
||||||
await fs.mkdirp(defaultPluginsIndexDir);
|
await fs.mkdirp(defaultPluginsIndexDir);
|
||||||
await fs.writeJSON(path.join(defaultPluginsIndexDir, 'index.json'), plugins);
|
await fs.writeJSON(
|
||||||
const pluginRequres = plugins
|
path.join(defaultPluginsIndexDir, 'index.json'),
|
||||||
|
bundledPlugins,
|
||||||
|
);
|
||||||
|
const pluginRequres = bundledPlugins
|
||||||
.map((x) => ` '${x.name}': require('${x.name}')`)
|
.map((x) => ` '${x.name}': require('${x.name}')`)
|
||||||
.join(',\n');
|
.join(',\n');
|
||||||
const generatedIndex = `
|
const generatedIndex = `
|
||||||
|
|||||||
Reference in New Issue
Block a user