From e42e44c324918ecb5287052242b884eb5bff1e1c Mon Sep 17 00:00:00 2001 From: Anton Nikolaev Date: Thu, 6 Feb 2020 11:12:28 -0800 Subject: [PATCH] Support installation of plugins packaged as vsix Summary: This is experimental feature to prove that vsix format can be used to publish plugins. Turns out there are almost no differences of vsix in comparison with npm packages. Reviewed By: jknoxville Differential Revision: D19770476 fbshipit-source-id: b3c62e7f2a4e8000113b9f1651e8657eb3e0d6fa --- package.json | 2 ++ src/utils/pluginManager.tsx | 28 ++++++++++++++++++++++------ static/index.js | 14 ++++++++++---- types/decompress-unzip.d.tsx | 12 ++++++++++++ 4 files changed, 46 insertions(+), 10 deletions(-) create mode 100644 types/decompress-unzip.d.tsx diff --git a/package.json b/package.json index b41f2bd94..ba0f90457 100644 --- a/package.json +++ b/package.json @@ -136,6 +136,7 @@ "dashify": "^2.0.0", "decompress": "^4.2.0", "decompress-targz": "^4.1.1", + "decompress-unzip": "^4.0.1", "deep-equal": "^2.0.1", "detect-port": "^1.1.1", "electron-devtools-installer": "^2.2.0", @@ -207,6 +208,7 @@ "rm-temp": "rimraf $TMPDIR/jest* $TMPDIR/react-native-packager*", "reset": "yarn rm-dist && yarn rm-temp && yarn cache clean && yarn rm-modules", "start": "cross-env NODE_ENV=development node scripts/start-dev-server.js", + "start:no-embedded-plugins": "cross-env NODE_ENV=development cross-env FLIPPER_NO_EMBEDDED_PLUGINS=true node scripts/start-dev-server.js", "build": "yarn rm-dist && cross-env NODE_ENV=production node scripts/build-release.js $@", "build-headless": "yarn rm-dist && mkdir dist && cross-env NODE_ENV=production node scripts/build-headless.js $@", "fix": "eslint . --fix --ext .js,.tsx", diff --git a/src/utils/pluginManager.tsx b/src/utils/pluginManager.tsx index fa96b6cbe..988d66db3 100644 --- a/src/utils/pluginManager.tsx +++ b/src/utils/pluginManager.tsx @@ -17,6 +17,7 @@ import NpmApi, {Package} from 'npm-api'; import semver from 'semver'; import decompress from 'decompress'; import decompressTargz from 'decompress-targz'; +import decompressUnzip from 'decompress-unzip'; import tmp from 'tmp'; const ALGOLIA_APPLICATION_ID = 'OFCNCOG2CU'; @@ -68,27 +69,42 @@ export async function installPluginFromFile(packagePath: string) { const tmpDir = tmp.dirSync().name; try { const files = await decompress(packagePath, tmpDir, { - plugins: [decompressTargz()], + plugins: [decompressTargz(), decompressUnzip()], }); if (!files.length) { throw new Error('The package is not in tar.gz format or is empty'); } + + // npm packages are tar.gz archives containing folder 'package' inside const packageDir = path.join(tmpDir, 'package'); - if (!(await fs.pathExists(packageDir))) { + const isNpmPackage = await fs.pathExists(packageDir); + + // vsix packages are zip archives containing folder 'extension' inside + const extensionDir = path.join(tmpDir, 'extension'); + const isVsix = await fs.pathExists(extensionDir); + + if (!isNpmPackage && !isVsix) { throw new Error( - 'Package format is invalid: directory "package" not found', + 'Package format is invalid: directory "package" or "extensions" not found in the archive root', ); } - const packageJsonPath = path.join(packageDir, 'package.json'); + + const packageRoot = isNpmPackage ? packageDir : extensionDir; + + // otherwise both npm and vsix are quite similar, so we can use the same logic for installing them + const packageJsonPath = path.join(packageRoot, 'package.json'); if (!(await fs.pathExists(packageJsonPath))) { throw new Error( - 'Package format is invalid: file "package/package.json" not found', + `Package format is invalid: file "${path.relative( + tmpDir, + packageJsonPath, + )}" not found`, ); } const packageJson = await fs.readJSON(packageJsonPath); const name = packageJson.name as string; await installPlugin(name, pluginManager => - pluginManager.installFromPath(packageDir).then(() => {}), + pluginManager.installFromPath(packageRoot).then(() => {}), ); } finally { if (fs.existsSync(tmpDir)) { diff --git a/static/index.js b/static/index.js index 5853994f9..21db19cdb 100644 --- a/static/index.js +++ b/static/index.js @@ -74,12 +74,18 @@ const argv = yargs const {config, configPath, flipperDir} = setup(argv); +const skipLoadingEmbeddedPlugins = process.env.FLIPPER_NO_EMBEDDED_PLUGINS; + const pluginPaths = config.pluginPaths - .concat( + .concat([ path.join(configPath, '..', 'thirdparty'), - path.join(__dirname, '..', 'src', 'plugins'), - path.join(__dirname, '..', 'src', 'fb', 'plugins'), - ) + ...(skipLoadingEmbeddedPlugins + ? [] + : [ + path.join(__dirname, '..', 'src', 'plugins'), + path.join(__dirname, '..', 'src', 'fb', 'plugins'), + ]), + ]) .map(expandTilde) .filter(fs.existsSync); diff --git a/types/decompress-unzip.d.tsx b/types/decompress-unzip.d.tsx new file mode 100644 index 000000000..43baefa23 --- /dev/null +++ b/types/decompress-unzip.d.tsx @@ -0,0 +1,12 @@ +/** + * 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 + */ + +declare module 'decompress-unzip' { + export default function(): any; +}