/** * 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 path from 'path'; import fs from 'fs-extra'; import {homedir} from 'os'; import {PluginMap, PluginDefinition} from '../reducers/pluginManager'; import {PluginManager as PM} from 'live-plugin-manager'; import algoliasearch from 'algoliasearch'; import NpmApi, {Package} from 'npm-api'; import semver from 'semver'; const ALGOLIA_APPLICATION_ID = 'OFCNCOG2CU'; const ALGOLIA_API_KEY = 'f54e21fa3a2a0160595bb058179bfb1e'; // TODO(T57014856): The use should be constrained to just this module when the // refactor is done. export function providePluginManager(): PM { return new PM({ ignoredDependencies: ['flipper', 'react', 'react-dom', '@types/*'], }); } // TODO(T57014856): This should be private, too. export function provideSearchIndex(): algoliasearch.Index { const client = algoliasearch(ALGOLIA_APPLICATION_ID, ALGOLIA_API_KEY); return client.initIndex('npm-search'); } export const PLUGIN_DIR = path.join(homedir(), '.flipper', 'thirdparty'); export async function readInstalledPlugins(): Promise { const pluginDirExists = await fs.pathExists(PLUGIN_DIR); if (!pluginDirExists) { return new Map(); } const dirs = await fs.readdir(PLUGIN_DIR); const plugins = await Promise.all<[string, PluginDefinition]>( dirs.map( name => new Promise(async (resolve, reject) => { if (!(await fs.lstat(path.join(PLUGIN_DIR, name))).isDirectory()) { return resolve(undefined); } const packageJSON = await fs.readFile( path.join(PLUGIN_DIR, name, 'package.json'), ); try { resolve([name, JSON.parse(packageJSON.toString())]); } catch (e) { reject(e); } }), ), ); return new Map(plugins.filter(Boolean)); } export type UpdateResult = | {kind: 'up-to-date'} | {kind: 'error'; error: Error} | {kind: 'update-available'; version: string}; export async function findPluginUpdates( currentPlugins: PluginMap, ): Promise<[string, UpdateResult][]> { const npm = new NpmApi(); return Promise.all( Array.from(currentPlugins.values()).map( async ( currentPlugin: PluginDefinition, ): Promise<[string, UpdateResult]> => npm .repo(currentPlugin.name) .package() .then((pkg: Package): [string, UpdateResult] => { if (semver.lt(currentPlugin.version, pkg.version)) { return [ currentPlugin.name, {kind: 'update-available', version: pkg.version}, ]; } else { return [currentPlugin.name, {kind: 'up-to-date'}]; } }) .catch(err => [currentPlugin.name, {kind: 'error', error: err}]), ), ); }