Plugin Marketplace API
Summary: Extracted plugin marketplace API to a separate file and updated it to load full plugin manifests. Reviewed By: passy Differential Revision: D25181759 fbshipit-source-id: a63f9ce16249ccc170df148cef5c209fdc6d4d6d
This commit is contained in:
committed by
Facebook GitHub Bot
parent
658b3e8a91
commit
5b26f36672
60
desktop/app/src/utils/testUtils.tsx
Normal file
60
desktop/app/src/utils/testUtils.tsx
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*
|
||||||
|
* @format
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {DownloadablePluginDetails} from 'flipper-plugin-lib';
|
||||||
|
|
||||||
|
export function createMockDownloadablePluginDetails(
|
||||||
|
params: {
|
||||||
|
id?: string;
|
||||||
|
name?: string;
|
||||||
|
version?: string;
|
||||||
|
title?: string;
|
||||||
|
flipperEngineVersion?: string;
|
||||||
|
downloadUrl?: string;
|
||||||
|
gatekeeper?: string;
|
||||||
|
lastUpdated?: Date;
|
||||||
|
} = {},
|
||||||
|
): DownloadablePluginDetails {
|
||||||
|
const {id, version, title, flipperEngineVersion, gatekeeper, lastUpdated} = {
|
||||||
|
id: 'test',
|
||||||
|
version: '3.0.1',
|
||||||
|
flipperEngineVersion: '0.46.0',
|
||||||
|
lastUpdated: new Date(1591226525 * 1000),
|
||||||
|
...params,
|
||||||
|
};
|
||||||
|
const lowercasedID = id.toLowerCase();
|
||||||
|
const name = params.name || `flipper-plugin-${lowercasedID}`;
|
||||||
|
const details: DownloadablePluginDetails = {
|
||||||
|
name: name || `flipper-plugin-${lowercasedID}`,
|
||||||
|
id: id,
|
||||||
|
bugs: {
|
||||||
|
email: 'bugs@localhost',
|
||||||
|
url: 'bugs.localhost',
|
||||||
|
},
|
||||||
|
category: 'tools',
|
||||||
|
description: 'Description of Test Plugin',
|
||||||
|
dir: `/Users/mock/.flipper/thirdparty/${name}`,
|
||||||
|
entry: `/Users/mock/.flipper/thirdparty/${name}/dist/bundle.js`,
|
||||||
|
flipperSDKVersion: flipperEngineVersion,
|
||||||
|
engines: {
|
||||||
|
flipper: flipperEngineVersion,
|
||||||
|
},
|
||||||
|
gatekeeper: gatekeeper ?? `GK_${lowercasedID}`,
|
||||||
|
icon: 'internet',
|
||||||
|
isDefault: false,
|
||||||
|
main: 'dist/bundle.js',
|
||||||
|
source: 'src/index.tsx',
|
||||||
|
specVersion: 2,
|
||||||
|
title: title ?? id,
|
||||||
|
version: version,
|
||||||
|
downloadUrl: `http://localhost/${lowercasedID}/${version}`,
|
||||||
|
lastUpdated: lastUpdated,
|
||||||
|
};
|
||||||
|
return details;
|
||||||
|
}
|
||||||
@@ -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 {getPluginDetails} from 'flipper-plugin-lib';
|
import {getPluginDetailsFromDir} 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 getPluginDetails(inputDirectory);
|
const plugin = await getPluginDetailsFromDir(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 {getPluginDetails} from 'flipper-plugin-lib';
|
import {getPluginDetailsFromDir} 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 getPluginDetails(inputDirectory);
|
const plugin = await getPluginDetailsFromDir(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}.`);
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default interface PluginDetails {
|
export interface PluginDetails {
|
||||||
dir: string;
|
dir: string;
|
||||||
name: string;
|
name: string;
|
||||||
specVersion: number;
|
specVersion: number;
|
||||||
@@ -22,9 +22,19 @@ export default interface PluginDetails {
|
|||||||
icon?: string;
|
icon?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
category?: string;
|
category?: string;
|
||||||
|
engines?: {
|
||||||
|
[name: string]: string;
|
||||||
|
};
|
||||||
bugs?: {
|
bugs?: {
|
||||||
email?: string;
|
email?: string;
|
||||||
url?: string;
|
url?: string;
|
||||||
};
|
};
|
||||||
flipperSDKVersion?: string;
|
flipperSDKVersion?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DownloadablePluginDetails extends PluginDetails {
|
||||||
|
downloadUrl: string;
|
||||||
|
lastUpdated: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PluginDetails;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import getPluginDetails from '../getPluginDetails';
|
import {getPluginDetailsFromDir} 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 getPluginDetails(pluginPath);
|
const details = await getPluginDetailsFromDir(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(`
|
||||||
@@ -69,7 +69,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 getPluginDetails(pluginPath);
|
const details = await getPluginDetailsFromDir(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(`
|
||||||
@@ -107,7 +107,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 getPluginDetails(pluginPath);
|
const details = await getPluginDetailsFromDir(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(`
|
||||||
@@ -144,7 +144,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 getPluginDetails(pluginPath);
|
const details = await getPluginDetailsFromDir(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(`
|
||||||
@@ -184,7 +184,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 getPluginDetails(pluginPath);
|
const details = await getPluginDetailsFromDir(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(`
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
pluginInstallationDir,
|
pluginInstallationDir,
|
||||||
} from './pluginPaths';
|
} from './pluginPaths';
|
||||||
import PluginDetails from './PluginDetails';
|
import PluginDetails from './PluginDetails';
|
||||||
import getPluginDetails from './getPluginDetails';
|
import {getPluginDetailsFromDir} from './getPluginDetails';
|
||||||
import pmap from 'p-map';
|
import pmap from 'p-map';
|
||||||
import {notNull} from './typeUtils';
|
import {notNull} from './typeUtils';
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ async function getFullyInstalledPlugins(): Promise<PluginDetails[]> {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return await getPluginDetails(pluginDir);
|
return await getPluginDetailsFromDir(pluginDir);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`Failed to load plugin from ${pluginDir}`, e);
|
console.error(`Failed to load plugin from ${pluginDir}`, e);
|
||||||
return undefined;
|
return undefined;
|
||||||
@@ -71,7 +71,7 @@ async function getPendingInstallationPlugins(): Promise<PluginDetails[]> {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return await getPluginDetails(pluginDir);
|
return await getPluginDetailsFromDir(pluginDir);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`Failed to load plugin from ${pluginDir}`, e);
|
console.error(`Failed to load plugin from ${pluginDir}`, e);
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|||||||
@@ -9,15 +9,10 @@
|
|||||||
|
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import PluginDetails from './PluginDetails';
|
import {PluginDetails} from './PluginDetails';
|
||||||
import {pluginCacheDir} from './pluginPaths';
|
import {getPluginInstallationDir, pluginCacheDir} from './pluginPaths';
|
||||||
|
|
||||||
export default async function (
|
export async function getPluginDetails(pluginDir: string, packageJson: any) {
|
||||||
pluginDir: string,
|
|
||||||
packageJson?: any,
|
|
||||||
): Promise<PluginDetails> {
|
|
||||||
packageJson =
|
|
||||||
packageJson || (await fs.readJson(path.join(pluginDir, 'package.json')));
|
|
||||||
const specVersion =
|
const specVersion =
|
||||||
packageJson.$schema &&
|
packageJson.$schema &&
|
||||||
packageJson.$schema ===
|
packageJson.$schema ===
|
||||||
@@ -34,6 +29,31 @@ export default async function (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getPluginDetailsFromDir(
|
||||||
|
pluginDir: string,
|
||||||
|
): Promise<PluginDetails> {
|
||||||
|
const packageJson = await fs.readJson(path.join(pluginDir, 'package.json'));
|
||||||
|
return await getPluginDetails(pluginDir, packageJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getPluginDetailsFromPackageJson(packageJson: any) {
|
||||||
|
const pluginDir = getPluginInstallationDir(packageJson.name);
|
||||||
|
return await getPluginDetails(pluginDir, packageJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getDownloadablePluginDetails(
|
||||||
|
packageJson: any,
|
||||||
|
downloadUrl: string,
|
||||||
|
lastUpdated: Date,
|
||||||
|
) {
|
||||||
|
const details = await getPluginDetailsFromPackageJson(packageJson);
|
||||||
|
return {
|
||||||
|
...details,
|
||||||
|
downloadUrl,
|
||||||
|
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(
|
async function getPluginDetailsV1(
|
||||||
pluginDir: string,
|
pluginDir: string,
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import {getInstalledPlugins} from './getInstalledPlugins';
|
|||||||
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 getPluginDetails from './getPluginDetails';
|
import {getPluginDetails} from './getPluginDetails';
|
||||||
import {getPluginInstallationDir} from './pluginInstaller';
|
import {getPluginInstallationDir} from './pluginPaths';
|
||||||
import pmap from 'p-map';
|
import pmap from 'p-map';
|
||||||
import {notNull} from './typeUtils';
|
import {notNull} from './typeUtils';
|
||||||
const npmApi = new NpmApi();
|
const npmApi = new NpmApi();
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export {default as PluginDetails} from './PluginDetails';
|
export * from './PluginDetails';
|
||||||
export {default as getPluginDetails} from './getPluginDetails';
|
export * from './getPluginDetails';
|
||||||
export * from './pluginInstaller';
|
export * from './pluginInstaller';
|
||||||
export * from './getInstalledPlugins';
|
export * from './getInstalledPlugins';
|
||||||
export * from './getUpdatablePlugins';
|
export * from './getUpdatablePlugins';
|
||||||
export * from './getSourcePlugins';
|
export * from './getSourcePlugins';
|
||||||
export {getPluginSourceFolders} from './pluginPaths';
|
export * from './pluginPaths';
|
||||||
|
|||||||
@@ -16,10 +16,14 @@ 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 PluginDetails from './PluginDetails';
|
||||||
import getPluginDetails from './getPluginDetails';
|
import {getPluginDetailsFromDir} from './getPluginDetails';
|
||||||
import {
|
import {
|
||||||
|
getPluginInstallationDir,
|
||||||
|
getPluginPendingInstallationDir,
|
||||||
|
getPluginPendingInstallationsDir,
|
||||||
pluginInstallationDir,
|
pluginInstallationDir,
|
||||||
pluginPendingInstallationDir,
|
pluginPendingInstallationDir,
|
||||||
|
getPluginDirNameFromPackageName,
|
||||||
} from './pluginPaths';
|
} from './pluginPaths';
|
||||||
import semver from 'semver';
|
import semver from 'semver';
|
||||||
|
|
||||||
@@ -29,35 +33,10 @@ function providePluginManagerNoDependencies(): PM {
|
|||||||
return new PM({ignoredDependencies: [/.*/]});
|
return new PM({ignoredDependencies: [/.*/]});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPluginPendingInstallationDir(
|
|
||||||
name: string,
|
|
||||||
version: string,
|
|
||||||
): string {
|
|
||||||
return path.join(getPluginPendingInstallationsDir(name), version);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPluginPendingInstallationsDir(name: string): string {
|
|
||||||
return path.join(
|
|
||||||
pluginPendingInstallationDir,
|
|
||||||
replaceInvalidPathSegmentCharacters(name),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getPluginInstallationDir(name: string): string {
|
|
||||||
return path.join(
|
|
||||||
pluginInstallationDir,
|
|
||||||
replaceInvalidPathSegmentCharacters(name),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function replaceInvalidPathSegmentCharacters(name: string) {
|
|
||||||
return name.replace('/', '__');
|
|
||||||
}
|
|
||||||
|
|
||||||
async function installPluginFromTempDir(
|
async function installPluginFromTempDir(
|
||||||
sourceDir: string,
|
sourceDir: string,
|
||||||
): Promise<PluginDetails> {
|
): Promise<PluginDetails> {
|
||||||
const pluginDetails = await getPluginDetails(sourceDir);
|
const pluginDetails = await getPluginDetailsFromDir(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 installationsDir = getPluginPendingInstallationsDir(name);
|
const installationsDir = getPluginPendingInstallationsDir(name);
|
||||||
@@ -93,7 +72,7 @@ async function installPluginFromTempDir(
|
|||||||
}
|
}
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
return await getPluginDetails(destinationDir);
|
return await getPluginDetailsFromDir(destinationDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getPluginRootDir(dir: string) {
|
async function getPluginRootDir(dir: string) {
|
||||||
@@ -121,7 +100,7 @@ export async function getInstalledPlugin(
|
|||||||
if (!(await fs.pathExists(dir))) {
|
if (!(await fs.pathExists(dir))) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return await getPluginDetails(dir);
|
return await getPluginDetailsFromDir(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function isPluginPendingInstallation(
|
export async function isPluginPendingInstallation(
|
||||||
@@ -140,7 +119,7 @@ export async function installPluginFromNpm(name: string) {
|
|||||||
await plugManNoDep.install(name);
|
await plugManNoDep.install(name);
|
||||||
const pluginTempDir = path.join(
|
const pluginTempDir = path.join(
|
||||||
tmpDir,
|
tmpDir,
|
||||||
replaceInvalidPathSegmentCharacters(name),
|
getPluginDirNameFromPackageName(name),
|
||||||
);
|
);
|
||||||
await installPluginFromTempDir(pluginTempDir);
|
await installPluginFromTempDir(pluginTempDir);
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import {homedir} from 'os';
|
|||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import expandTilde from 'expand-tilde';
|
import expandTilde from 'expand-tilde';
|
||||||
|
|
||||||
export const flipperDataDir = path.join(homedir(), '.flipper');
|
const flipperDataDir = path.join(homedir(), '.flipper');
|
||||||
|
|
||||||
export const pluginInstallationDir = path.join(flipperDataDir, 'thirdparty');
|
export const pluginInstallationDir = path.join(flipperDataDir, 'thirdparty');
|
||||||
|
|
||||||
@@ -42,3 +42,28 @@ export async function getPluginSourceFolders(): Promise<string[]> {
|
|||||||
pluginFolders.push(path.resolve(__dirname, '..', '..', 'plugins', 'fb'));
|
pluginFolders.push(path.resolve(__dirname, '..', '..', 'plugins', 'fb'));
|
||||||
return pluginFolders.map(expandTilde).filter(fs.existsSync);
|
return pluginFolders.map(expandTilde).filter(fs.existsSync);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getPluginPendingInstallationDir(
|
||||||
|
name: string,
|
||||||
|
version: string,
|
||||||
|
): string {
|
||||||
|
return path.join(getPluginPendingInstallationsDir(name), version);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPluginPendingInstallationsDir(name: string): string {
|
||||||
|
return path.join(
|
||||||
|
pluginPendingInstallationDir,
|
||||||
|
getPluginDirNameFromPackageName(name),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPluginInstallationDir(name: string): string {
|
||||||
|
return path.join(
|
||||||
|
pluginInstallationDir,
|
||||||
|
getPluginDirNameFromPackageName(name),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPluginDirNameFromPackageName(name: string) {
|
||||||
|
return name.replace('/', '__');
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user