Summary: Allow splitting package.json to public one and fb-internal one located in "fb/package.json". When plugin is packaged, fields in package.json are overwritten by fields from "fb/package.json" if they exist. This give us a way to specify additional metadata which only make sense internally (e.g. oncall and internal links to docs and support). Reviewed By: mweststrate Differential Revision: D28542101 fbshipit-source-id: c0167461897a994e5731aaf0fe625de052eda864
149 lines
4.3 KiB
TypeScript
149 lines
4.3 KiB
TypeScript
/**
|
|
* 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 fs from 'fs-extra';
|
|
import path from 'path';
|
|
import {
|
|
DownloadablePluginDetails,
|
|
InstalledPluginDetails,
|
|
PluginDetails,
|
|
} from './PluginDetails';
|
|
import {pluginCacheDir} from './pluginPaths';
|
|
|
|
export async function readPluginPackageJson(dir: string): Promise<any> {
|
|
const baseJson = await fs.readJson(path.join(dir, 'package.json'));
|
|
if (await fs.pathExists(path.join(dir, 'fb', 'package.json'))) {
|
|
const addedJson = await fs.readJson(path.join(dir, 'fb', 'package.json'));
|
|
return Object.assign({}, baseJson, addedJson);
|
|
} else {
|
|
return baseJson;
|
|
}
|
|
}
|
|
|
|
export function isPluginJson(packageJson: any): boolean {
|
|
return packageJson?.keywords?.includes('flipper-plugin');
|
|
}
|
|
|
|
export async function isPluginDir(dir: string): Promise<boolean> {
|
|
const packageJsonPath = path.join(dir, 'package.json');
|
|
const json = (await fs.pathExists(packageJsonPath))
|
|
? await fs.readJson(path.join(dir, 'package.json'), {
|
|
throws: false,
|
|
})
|
|
: undefined;
|
|
return isPluginJson(json);
|
|
}
|
|
|
|
export function getPluginDetails(packageJson: any): PluginDetails {
|
|
const specVersion =
|
|
packageJson.$schema &&
|
|
packageJson.$schema ===
|
|
'https://fbflipper.com/schemas/plugin-package/v2.json'
|
|
? 2
|
|
: 1;
|
|
switch (specVersion) {
|
|
case 1:
|
|
return getPluginDetailsV1(packageJson);
|
|
case 2:
|
|
return getPluginDetailsV2(packageJson);
|
|
default:
|
|
throw new Error(`Unknown plugin format version: ${specVersion}`);
|
|
}
|
|
}
|
|
|
|
export async function getInstalledPluginDetails(
|
|
dir: string,
|
|
packageJson?: any,
|
|
): Promise<InstalledPluginDetails> {
|
|
packageJson = packageJson ?? (await readPluginPackageJson(dir));
|
|
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 function getDownloadablePluginDetails(
|
|
packageJson: any,
|
|
downloadUrl: string,
|
|
lastUpdated: Date,
|
|
): DownloadablePluginDetails {
|
|
const details = getPluginDetails(packageJson);
|
|
return {
|
|
...details,
|
|
isBundled: false,
|
|
isActivatable: false,
|
|
downloadUrl,
|
|
lastUpdated,
|
|
};
|
|
}
|
|
|
|
// Plugins packaged using V1 are distributed as sources and compiled in run-time.
|
|
function getPluginDetailsV1(packageJson: any): PluginDetails {
|
|
return {
|
|
specVersion: 1,
|
|
name: packageJson.name,
|
|
version: packageJson.version,
|
|
main: 'dist/bundle.js',
|
|
source: packageJson.main,
|
|
id: packageJson.name,
|
|
gatekeeper: packageJson.gatekeeper,
|
|
icon: packageJson.icon,
|
|
title: packageJson.title || packageJson.name,
|
|
description: packageJson.description,
|
|
category: packageJson.category,
|
|
bugs: packageJson.bugs,
|
|
flipperSDKVersion: packageJson?.peerDependencies?.['flipper-plugin'],
|
|
pluginType: packageJson?.pluginType,
|
|
supportedDevices: packageJson?.supportedDevices,
|
|
engines: packageJson.engines,
|
|
};
|
|
}
|
|
|
|
// Plugins packaged using V2 are pre-bundled, so compilation in run-time is not required for them.
|
|
function getPluginDetailsV2(packageJson: any): PluginDetails {
|
|
return {
|
|
specVersion: 2,
|
|
name: packageJson.name,
|
|
version: packageJson.version,
|
|
main: packageJson.main,
|
|
source: packageJson.flipperBundlerEntry,
|
|
id: packageJson.id || packageJson.name,
|
|
gatekeeper: packageJson.gatekeeper,
|
|
icon: packageJson.icon,
|
|
title:
|
|
packageJson.title || packageJson.id || getTitleFromName(packageJson.name),
|
|
description: packageJson.description,
|
|
category: packageJson.category,
|
|
bugs: packageJson.bugs,
|
|
flipperSDKVersion: packageJson?.peerDependencies?.['flipper-plugin'],
|
|
pluginType: packageJson?.pluginType,
|
|
supportedDevices: packageJson?.supportedDevices,
|
|
engines: packageJson.engines,
|
|
};
|
|
}
|
|
|
|
function getTitleFromName(name: string): string {
|
|
const prefix = 'flipper-plugin-';
|
|
if (name.startsWith(prefix)) {
|
|
return name.substr(prefix.length);
|
|
}
|
|
return name;
|
|
}
|