Install pre-bundled packages 1/N
Summary: No functional changes in this diff. This is just plugin manager refactoring before implementing new functionality for installing pre-bundled packages. Reviewed By: passy Differential Revision: D19832078 fbshipit-source-id: 56b7ff1c68b6beb4abb2941da607651268e5f71a
This commit is contained in:
committed by
Facebook Github Bot
parent
463e8a7984
commit
71928fdf08
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
|
import {promisify} from 'util';
|
||||||
import {homedir} from 'os';
|
import {homedir} from 'os';
|
||||||
import {PluginMap, PluginDefinition} from '../reducers/pluginManager';
|
import {PluginMap, PluginDefinition} from '../reducers/pluginManager';
|
||||||
import {PluginManager as PM} from 'live-plugin-manager';
|
import {PluginManager as PM} from 'live-plugin-manager';
|
||||||
@@ -20,23 +21,30 @@ import decompressTargz from 'decompress-targz';
|
|||||||
import decompressUnzip from 'decompress-unzip';
|
import decompressUnzip from 'decompress-unzip';
|
||||||
import tmp from 'tmp';
|
import tmp from 'tmp';
|
||||||
|
|
||||||
|
const getTmpDir = promisify(tmp.dir) as () => Promise<string>;
|
||||||
|
|
||||||
const ALGOLIA_APPLICATION_ID = 'OFCNCOG2CU';
|
const ALGOLIA_APPLICATION_ID = 'OFCNCOG2CU';
|
||||||
const ALGOLIA_API_KEY = 'f54e21fa3a2a0160595bb058179bfb1e';
|
const ALGOLIA_API_KEY = 'f54e21fa3a2a0160595bb058179bfb1e';
|
||||||
|
|
||||||
export const PLUGIN_DIR = path.join(homedir(), '.flipper', 'thirdparty');
|
export const PLUGIN_DIR = path.join(homedir(), '.flipper', 'thirdparty');
|
||||||
|
|
||||||
// TODO(T57014856): The use should be constrained to just this module when the
|
function providePluginManager(): PM {
|
||||||
// refactor is done.
|
|
||||||
export function providePluginManager(): PM {
|
|
||||||
return new PM({
|
return new PM({
|
||||||
ignoredDependencies: [/^flipper$/, /^react$/, /^react-dom$/, /^@types/],
|
ignoredDependencies: [/^flipper$/, /^react$/, /^react-dom$/, /^@types\//],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function installPlugin(
|
function providePluginManagerNoDependencies(): PM {
|
||||||
name: string,
|
return new PM({ignoredDependencies: [/.*/]});
|
||||||
installFn: (pluginManager: PM) => Promise<void>,
|
}
|
||||||
) {
|
|
||||||
|
async function installPlugin(pluginDir: string) {
|
||||||
|
const packageJSONPath = path.join(pluginDir, 'package.json');
|
||||||
|
const packageJSON = JSON.parse(
|
||||||
|
(await fs.readFile(packageJSONPath)).toString(),
|
||||||
|
);
|
||||||
|
const name = packageJSON.name;
|
||||||
|
|
||||||
await fs.ensureDir(PLUGIN_DIR);
|
await fs.ensureDir(PLUGIN_DIR);
|
||||||
// create empty watchman config (required by metro's file watcher)
|
// create empty watchman config (required by metro's file watcher)
|
||||||
await fs.writeFile(path.join(PLUGIN_DIR, '.watchmanconfig'), '{}');
|
await fs.writeFile(path.join(PLUGIN_DIR, '.watchmanconfig'), '{}');
|
||||||
@@ -45,42 +53,34 @@ async function installPlugin(
|
|||||||
await fs.remove(destinationDir);
|
await fs.remove(destinationDir);
|
||||||
|
|
||||||
const pluginManager = providePluginManager();
|
const pluginManager = providePluginManager();
|
||||||
// install the plugin and all it's dependencies into node_modules
|
// install the plugin dependencies into node_modules
|
||||||
pluginManager.options.pluginsPath = path.join(destinationDir, 'node_modules');
|
const nodeModulesDir = path.join(destinationDir, 'node_modules');
|
||||||
await installFn(pluginManager);
|
pluginManager.options.pluginsPath = nodeModulesDir;
|
||||||
|
const pluginInfo = await pluginManager.installFromPath(pluginDir);
|
||||||
|
|
||||||
// move the plugin itself out of the node_modules folder
|
const itselfDir = path.join(nodeModulesDir, name);
|
||||||
const pluginDir = path.join(PLUGIN_DIR, name, 'node_modules', name);
|
|
||||||
const pluginFiles = await fs.readdir(pluginDir);
|
// copying plugin files into the destination folder
|
||||||
|
const pluginFiles = await fs.readdir(itselfDir);
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
pluginFiles.map(f =>
|
pluginFiles.map(f =>
|
||||||
fs.move(path.join(pluginDir, f), path.join(pluginDir, '..', '..', f)),
|
fs.move(path.join(itselfDir, f), path.join(destinationDir, f)),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// live-plugin-manager also installs plugin itself into the target dir, it's better remove it
|
||||||
|
await fs.remove(itselfDir);
|
||||||
|
|
||||||
|
return pluginInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function installPluginFromNpm(name: string) {
|
async function getPluginRootDir(dir: string) {
|
||||||
await installPlugin(name, pluginManager =>
|
|
||||||
pluginManager.install(name).then(() => {}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function installPluginFromFile(packagePath: string) {
|
|
||||||
const tmpDir = tmp.dirSync().name;
|
|
||||||
try {
|
|
||||||
const files = await decompress(packagePath, tmpDir, {
|
|
||||||
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
|
// npm packages are tar.gz archives containing folder 'package' inside
|
||||||
const packageDir = path.join(tmpDir, 'package');
|
const packageDir = path.join(dir, 'package');
|
||||||
const isNpmPackage = await fs.pathExists(packageDir);
|
const isNpmPackage = await fs.pathExists(packageDir);
|
||||||
|
|
||||||
// vsix packages are zip archives containing folder 'extension' inside
|
// vsix packages are zip archives containing folder 'extension' inside
|
||||||
const extensionDir = path.join(tmpDir, 'extension');
|
const extensionDir = path.join(dir, 'extension');
|
||||||
const isVsix = await fs.pathExists(extensionDir);
|
const isVsix = await fs.pathExists(extensionDir);
|
||||||
|
|
||||||
if (!isNpmPackage && !isVsix) {
|
if (!isNpmPackage && !isVsix) {
|
||||||
@@ -89,26 +89,39 @@ export async function installPluginFromFile(packagePath: string) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const packageRoot = isNpmPackage ? packageDir : extensionDir;
|
return 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 "${path.relative(
|
|
||||||
tmpDir,
|
|
||||||
packageJsonPath,
|
|
||||||
)}" not found`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
const packageJson = await fs.readJSON(packageJsonPath);
|
|
||||||
const name = packageJson.name as string;
|
export async function installPluginFromNpm(name: string) {
|
||||||
await installPlugin(name, pluginManager =>
|
const tmpDir = await getTmpDir();
|
||||||
pluginManager.installFromPath(packageRoot).then(() => {}),
|
try {
|
||||||
);
|
await fs.ensureDir(tmpDir);
|
||||||
|
const plugManNoDep = providePluginManagerNoDependencies();
|
||||||
|
plugManNoDep.options.pluginsPath = tmpDir;
|
||||||
|
await plugManNoDep.install(name);
|
||||||
|
const pluginDir = path.join(tmpDir, name);
|
||||||
|
return await installPlugin(pluginDir);
|
||||||
} finally {
|
} finally {
|
||||||
if (fs.existsSync(tmpDir)) {
|
if (await fs.pathExists(tmpDir)) {
|
||||||
fs.removeSync(tmpDir);
|
await fs.remove(tmpDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function installPluginFromFile(packagePath: string) {
|
||||||
|
const tmpDir = await getTmpDir();
|
||||||
|
try {
|
||||||
|
const files = await decompress(packagePath, tmpDir, {
|
||||||
|
plugins: [decompressTargz(), decompressUnzip()],
|
||||||
|
});
|
||||||
|
if (!files.length) {
|
||||||
|
throw new Error('The package is not in tar.gz format or is empty');
|
||||||
|
}
|
||||||
|
const pluginDir = await getPluginRootDir(tmpDir);
|
||||||
|
return await installPlugin(pluginDir);
|
||||||
|
} finally {
|
||||||
|
if (await fs.pathExists(tmpDir)) {
|
||||||
|
await fs.remove(tmpDir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user