2017-07-20 01:55:40 +00:00
|
|
|
// @flow
|
2017-06-01 02:04:27 +00:00
|
|
|
import mkdirp from 'mkdirp';
|
2019-09-18 00:02:42 +00:00
|
|
|
import * as packageJson from '../../package.json';
|
2017-07-20 01:55:40 +00:00
|
|
|
import * as models from '../models';
|
2017-06-01 02:04:27 +00:00
|
|
|
import fs from 'fs';
|
|
|
|
import path from 'path';
|
2018-06-25 17:42:50 +00:00
|
|
|
import { PLUGIN_PATH } from '../common/constants';
|
|
|
|
import { resolveHomePath } from '../common/misc';
|
|
|
|
import { showError } from '../ui/components/modals/index';
|
|
|
|
import type { PluginTemplateTag } from '../templating/extensions/index';
|
2018-07-17 23:34:28 +00:00
|
|
|
import type { PluginTheme } from './misc';
|
2019-05-04 20:34:52 +00:00
|
|
|
import type { RequestGroup } from '../models/request-group';
|
|
|
|
import type { Request } from '../models/request';
|
2017-06-01 02:04:27 +00:00
|
|
|
|
2017-07-20 01:55:40 +00:00
|
|
|
export type Plugin = {
|
|
|
|
name: string,
|
2017-07-22 00:55:34 +00:00
|
|
|
description: string,
|
2017-07-20 01:55:40 +00:00
|
|
|
version: string,
|
|
|
|
directory: string,
|
2018-12-12 17:36:11 +00:00
|
|
|
module: *,
|
2017-07-20 01:55:40 +00:00
|
|
|
};
|
2017-06-01 02:04:27 +00:00
|
|
|
|
2017-07-20 01:55:40 +00:00
|
|
|
export type TemplateTag = {
|
2018-06-09 03:22:39 +00:00
|
|
|
plugin: Plugin,
|
2018-12-12 17:36:11 +00:00
|
|
|
templateTag: PluginTemplateTag,
|
2018-06-25 17:42:50 +00:00
|
|
|
};
|
2017-07-20 01:55:40 +00:00
|
|
|
|
2019-05-04 20:34:52 +00:00
|
|
|
export type RequestGroupAction = {
|
|
|
|
plugin: Plugin,
|
|
|
|
action: (
|
|
|
|
context: Object,
|
|
|
|
models: {
|
|
|
|
requestGroup: RequestGroup,
|
|
|
|
requests: Array<Request>,
|
|
|
|
},
|
|
|
|
) => void | Promise<void>,
|
|
|
|
label: string,
|
|
|
|
icon?: string,
|
|
|
|
};
|
|
|
|
|
2017-07-20 01:55:40 +00:00
|
|
|
export type RequestHook = {
|
|
|
|
plugin: Plugin,
|
2018-12-12 17:36:11 +00:00
|
|
|
hook: Function,
|
2018-06-25 17:42:50 +00:00
|
|
|
};
|
2017-07-20 01:55:40 +00:00
|
|
|
|
|
|
|
export type ResponseHook = {
|
|
|
|
plugin: Plugin,
|
2018-12-12 17:36:11 +00:00
|
|
|
hook: Function,
|
2018-06-25 17:42:50 +00:00
|
|
|
};
|
2017-07-20 01:55:40 +00:00
|
|
|
|
2018-07-17 23:34:28 +00:00
|
|
|
export type Theme = {
|
|
|
|
plugin: Plugin,
|
2018-12-12 17:36:11 +00:00
|
|
|
theme: PluginTheme,
|
2018-07-17 23:34:28 +00:00
|
|
|
};
|
|
|
|
|
2017-07-20 01:55:40 +00:00
|
|
|
let plugins: ?Array<Plugin> = null;
|
|
|
|
|
2018-06-25 17:42:50 +00:00
|
|
|
export async function init(): Promise<void> {
|
2017-06-01 02:04:27 +00:00
|
|
|
// Force plugins to load.
|
2017-07-20 01:55:40 +00:00
|
|
|
await getPlugins(true);
|
2017-06-01 02:04:27 +00:00
|
|
|
}
|
|
|
|
|
2018-06-25 17:42:50 +00:00
|
|
|
async function _traversePluginPath(pluginMap: Object, allPaths: Array<string>) {
|
2018-05-23 04:49:08 +00:00
|
|
|
for (const p of allPaths) {
|
|
|
|
if (!fs.existsSync(p)) {
|
|
|
|
continue;
|
2017-11-26 20:45:40 +00:00
|
|
|
}
|
|
|
|
|
2018-05-23 04:49:08 +00:00
|
|
|
for (const filename of fs.readdirSync(p)) {
|
|
|
|
try {
|
|
|
|
const modulePath = path.join(p, filename);
|
|
|
|
const packageJSONPath = path.join(modulePath, 'package.json');
|
2017-07-22 00:55:34 +00:00
|
|
|
|
2018-05-23 04:49:08 +00:00
|
|
|
// Only read directories
|
|
|
|
if (!fs.statSync(modulePath).isDirectory()) {
|
|
|
|
continue;
|
|
|
|
}
|
2017-09-17 14:04:46 +00:00
|
|
|
|
2018-05-23 04:49:08 +00:00
|
|
|
// Is it a scoped directory?
|
|
|
|
if (filename.startsWith('@')) {
|
|
|
|
await _traversePluginPath(pluginMap, [modulePath]);
|
|
|
|
}
|
2017-09-17 14:04:46 +00:00
|
|
|
|
2018-05-23 04:49:08 +00:00
|
|
|
// Is it a Node module?
|
|
|
|
if (!fs.readdirSync(modulePath).includes('package.json')) {
|
|
|
|
continue;
|
|
|
|
}
|
2017-09-17 14:04:46 +00:00
|
|
|
|
2018-05-23 04:49:08 +00:00
|
|
|
// Delete `require` cache if plugin has been required before
|
|
|
|
for (const p of Object.keys(global.require.cache)) {
|
|
|
|
if (p.indexOf(modulePath) === 0) {
|
|
|
|
delete global.require.cache[p];
|
2018-04-05 20:54:11 +00:00
|
|
|
}
|
2018-05-23 04:49:08 +00:00
|
|
|
}
|
2018-04-05 20:54:11 +00:00
|
|
|
|
2018-05-23 04:49:08 +00:00
|
|
|
// Use global.require() instead of require() because Webpack wraps require()
|
|
|
|
const pluginJson = global.require(packageJSONPath);
|
2017-09-17 14:04:46 +00:00
|
|
|
|
2018-05-23 04:49:08 +00:00
|
|
|
// Not an Insomnia plugin because it doesn't have the package.json['insomnia']
|
|
|
|
if (!pluginJson.hasOwnProperty('insomnia')) {
|
|
|
|
continue;
|
2017-07-22 00:55:34 +00:00
|
|
|
}
|
2018-05-23 04:49:08 +00:00
|
|
|
|
|
|
|
// Delete require cache entry and re-require
|
|
|
|
const module = global.require(modulePath);
|
|
|
|
|
2018-10-17 16:42:33 +00:00
|
|
|
pluginMap[pluginJson.name] = _initPlugin(pluginJson || {}, module, modulePath);
|
2018-05-23 04:49:08 +00:00
|
|
|
console.log(`[plugin] Loaded ${modulePath}`);
|
|
|
|
} catch (err) {
|
|
|
|
showError({
|
|
|
|
title: 'Plugin Error',
|
|
|
|
message: 'Failed to load plugin ' + filename,
|
2018-12-12 17:36:11 +00:00
|
|
|
error: err,
|
2018-05-23 04:49:08 +00:00
|
|
|
});
|
2017-06-01 02:04:27 +00:00
|
|
|
}
|
|
|
|
}
|
2018-05-23 04:49:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-17 16:42:33 +00:00
|
|
|
export async function getPlugins(force: boolean = false): Promise<Array<Plugin>> {
|
2018-05-23 04:49:08 +00:00
|
|
|
if (force) {
|
|
|
|
plugins = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!plugins) {
|
2018-06-06 20:27:53 +00:00
|
|
|
const settings = await models.settings.getOrCreate();
|
2018-06-25 17:42:50 +00:00
|
|
|
const extraPaths = settings.pluginPath
|
|
|
|
.split(':')
|
|
|
|
.filter(p => p)
|
|
|
|
.map(resolveHomePath);
|
2018-06-06 20:27:53 +00:00
|
|
|
|
|
|
|
// Make sure the default directories exist
|
|
|
|
mkdirp.sync(PLUGIN_PATH);
|
|
|
|
|
|
|
|
// Also look in node_modules folder in each directory
|
|
|
|
const basePaths = [PLUGIN_PATH, ...extraPaths];
|
|
|
|
const extendedPaths = basePaths.map(p => path.join(p, 'node_modules'));
|
|
|
|
const allPaths = [...basePaths, ...extendedPaths];
|
|
|
|
|
|
|
|
// Store plugins in a map so that plugins with the same
|
|
|
|
// name only get added once
|
|
|
|
// TODO: Make this more complex and have the latest version always win
|
|
|
|
const pluginMap: { [string]: Plugin } = {
|
|
|
|
// "name": "module"
|
|
|
|
};
|
|
|
|
|
2019-09-18 00:02:42 +00:00
|
|
|
for (const p of packageJson.app.plugins) {
|
2018-06-06 20:27:53 +00:00
|
|
|
const pluginJson = global.require(`${p}/package.json`);
|
|
|
|
const pluginModule = global.require(p);
|
|
|
|
pluginMap[pluginJson.name] = _initPlugin(pluginJson, pluginModule);
|
|
|
|
}
|
2017-06-01 02:04:27 +00:00
|
|
|
|
2018-06-06 20:27:53 +00:00
|
|
|
await _traversePluginPath(pluginMap, allPaths);
|
2018-05-23 04:49:08 +00:00
|
|
|
|
2018-06-06 20:27:53 +00:00
|
|
|
plugins = Object.keys(pluginMap).map(name => pluginMap[name]);
|
|
|
|
}
|
2018-05-23 04:49:08 +00:00
|
|
|
|
2018-06-06 20:27:53 +00:00
|
|
|
return plugins;
|
2017-06-01 02:04:27 +00:00
|
|
|
}
|
|
|
|
|
2019-05-04 20:34:52 +00:00
|
|
|
export async function getRequestGroupActions(): Promise<Array<RequestGroupAction>> {
|
|
|
|
let extensions = [];
|
|
|
|
for (const plugin of await getPlugins()) {
|
|
|
|
const actions = plugin.module.requestGroupActions || [];
|
|
|
|
extensions = [...extensions, ...actions.map(p => ({ plugin, ...p }))];
|
|
|
|
}
|
|
|
|
|
|
|
|
return extensions;
|
|
|
|
}
|
|
|
|
|
2018-06-25 17:42:50 +00:00
|
|
|
export async function getTemplateTags(): Promise<Array<TemplateTag>> {
|
2017-06-01 02:04:27 +00:00
|
|
|
let extensions = [];
|
2018-06-06 20:27:53 +00:00
|
|
|
for (const plugin of await getPlugins()) {
|
|
|
|
const templateTags = plugin.module.templateTags || [];
|
2018-10-17 16:42:33 +00:00
|
|
|
extensions = [...extensions, ...templateTags.map(tt => ({ plugin, templateTag: tt }))];
|
2018-06-06 20:27:53 +00:00
|
|
|
}
|
2017-06-01 02:04:27 +00:00
|
|
|
|
2018-06-06 20:27:53 +00:00
|
|
|
return extensions;
|
2017-06-01 02:04:27 +00:00
|
|
|
}
|
2017-07-20 01:55:40 +00:00
|
|
|
|
2018-06-25 17:42:50 +00:00
|
|
|
export async function getRequestHooks(): Promise<Array<RequestHook>> {
|
2017-07-20 01:55:40 +00:00
|
|
|
let functions = [];
|
2018-06-06 20:27:53 +00:00
|
|
|
for (const plugin of await getPlugins()) {
|
|
|
|
const moreFunctions = plugin.module.requestHooks || [];
|
2018-10-17 16:42:33 +00:00
|
|
|
functions = [...functions, ...moreFunctions.map(hook => ({ plugin, hook }))];
|
2018-06-06 20:27:53 +00:00
|
|
|
}
|
2017-07-20 01:55:40 +00:00
|
|
|
|
2018-06-06 20:27:53 +00:00
|
|
|
return functions;
|
2017-07-20 01:55:40 +00:00
|
|
|
}
|
|
|
|
|
2018-06-25 17:42:50 +00:00
|
|
|
export async function getResponseHooks(): Promise<Array<ResponseHook>> {
|
2017-07-20 01:55:40 +00:00
|
|
|
let functions = [];
|
2018-06-06 20:27:53 +00:00
|
|
|
for (const plugin of await getPlugins()) {
|
|
|
|
const moreFunctions = plugin.module.responseHooks || [];
|
2018-10-17 16:42:33 +00:00
|
|
|
functions = [...functions, ...moreFunctions.map(hook => ({ plugin, hook }))];
|
2018-06-06 20:27:53 +00:00
|
|
|
}
|
2017-07-20 01:55:40 +00:00
|
|
|
|
2018-06-06 20:27:53 +00:00
|
|
|
return functions;
|
2017-07-20 01:55:40 +00:00
|
|
|
}
|
2017-11-26 20:45:40 +00:00
|
|
|
|
2018-07-17 23:34:28 +00:00
|
|
|
export async function getThemes(): Promise<Array<Theme>> {
|
|
|
|
let extensions = [];
|
|
|
|
for (const plugin of await getPlugins()) {
|
|
|
|
const themes = plugin.module.themes || [];
|
|
|
|
extensions = [...extensions, ...themes.map(theme => ({ plugin, theme }))];
|
|
|
|
}
|
|
|
|
|
|
|
|
return extensions;
|
|
|
|
}
|
|
|
|
|
2018-06-25 17:42:50 +00:00
|
|
|
function _initPlugin(packageJSON: Object, module: any, path: ?string): Plugin {
|
2017-11-26 20:45:40 +00:00
|
|
|
const meta = packageJSON.insomnia || {};
|
|
|
|
return {
|
|
|
|
name: packageJSON.name || meta.name,
|
|
|
|
description: packageJSON.description || meta.description || '',
|
|
|
|
version: packageJSON.version || 'unknown',
|
|
|
|
directory: path || '',
|
2018-12-12 17:36:11 +00:00
|
|
|
module: module,
|
2017-11-26 20:45:40 +00:00
|
|
|
};
|
|
|
|
}
|