diff --git a/app/index.js b/app/index.js index 86c6a386..e58e3fcd 100644 --- a/app/index.js +++ b/app/index.js @@ -1,7 +1,6 @@ const { app, BrowserWindow, shell, Menu } = require('electron'); const createRPC = require('./rpc'); const createMenu = require('./menu'); -const Session = require('./session'); const genUid = require('uid2'); const { resolve } = require('path'); const isDev = require('electron-is-dev'); @@ -12,6 +11,7 @@ const toHex = require('convert-css-color-name-to-hex'); const config = require('./config'); config.init(); const plugins = require('./plugins'); +const Session = require('./session'); const windowSet = new Set([]); diff --git a/app/plugins.js b/app/plugins.js index 38a85e63..4804b723 100644 --- a/app/plugins.js +++ b/app/plugins.js @@ -16,6 +16,13 @@ const cache = new Config(); // modules path const path = resolve(homedir(), '.hyperterm_plugins'); const localPath = resolve(homedir(), '.hyperterm_plugins', 'local'); +const availableExtensions = new Set([ + 'onApp', 'onWindow', 'onUnload', 'middleware', + 'reduceUI', 'reduceSessions', 'decorateMenu', + 'decorateTerm', 'decorateHyperTerm', 'decorateTab', + 'decorateNotification', 'decorateNotifications', + 'decorateTabs', 'decorateConfig', 'decorateEnv' +]); // init plugin directories if not present mkdirpSync(path); @@ -239,14 +246,8 @@ function requirePlugins () { let mod; try { mod = require(path); - - if (!mod || (!mod.onApp && !mod.onWindow && !mod.onUnload && - !mod.middleware && !mod.reduceUI && !mod.reduceSessions && - !mod.decorateConfig && !mod.decorateMenu && - !mod.decorateTerm && !mod.decorateHyperTerm && - !mod.decorateTab && !mod.decorateNotification && - !mod.decorateNotifications && !mod.decorateTabs && - !mod.decorateConfig)) { + const exposed = mod && Object.keys(mod).some(key => availableExtensions.has(key)); + if (!exposed) { notify('Plugin error!', `Plugin "${basename(path)}" does not expose any ` + 'HyperTerm extension API methods'); return; @@ -282,48 +283,37 @@ exports.onWindow = function (win) { }); }; -exports.decorateMenu = function (tpl) { - let decorated = tpl; +// decorates the base object by calling plugin[key] +// for all the available plugins +function decorateObject (base, key) { + let decorated = base; modules.forEach((plugin) => { - if (plugin.decorateMenu) { - const res = plugin.decorateMenu(decorated); - if (res) { + if (plugin[key]) { + const res = plugin[key](decorated); + if (res && 'object' === typeof res) { decorated = res; } else { - console.error('incompatible response type for `decorateMenu`'); + notify('Plugin error!', `"${plugin._name}": invalid return type for \`${key}\``); } } }); + return decorated; +} + +exports.decorateMenu = function (tpl) { + return decorateObject(tpl, 'decorateMenu'); +}; + +exports.getDecoratedEnv = function (baseEnv) { + return decorateObject(baseEnv, 'decorateEnv'); }; exports.getDecoratedConfig = function () { - let decorated = config.getConfig(); - modules.forEach((plugin) => { - if (plugin.decorateConfig) { - const res = plugin.decorateConfig(decorated); - if (res && 'object' === typeof res) { - decorated = res; - } else { - notify('Plugin error!', `"${plugin._name}": invalid return type for \`decorateConfig\``); - } - } - }); - return decorated; + const baseConfig = config.getConfig(); + return decorateObject(baseConfig, 'decorateConfig'); }; exports.getDecoratedBrowserOptions = function (defaults) { - let decorated = defaults; - modules.forEach((plugin) => { - if (plugin.decorateBrowserOptions) { - const res = plugin.decorateBrowserOptions(decorated); - if (res && 'object' === typeof res) { - decorated = res; - } else { - notify('Plugin error!', `"${plugin._name}": invalid return type for \`decorateBrowserOptions\``); - } - } - }); - return decorated; + return decorateObject(defaults, 'decorateBrowserOptions'); }; - diff --git a/app/session.js b/app/session.js index c7ee468e..cdc27a62 100644 --- a/app/session.js +++ b/app/session.js @@ -2,6 +2,7 @@ const { app } = require('electron'); const { EventEmitter } = require('events'); const { exec } = require('child_process'); const defaultShell = require('default-shell'); +const { getDecoratedEnv } = require('./plugins'); const { productName, version } = require('./package'); let spawn; @@ -22,16 +23,18 @@ module.exports = class Session extends EventEmitter { constructor ({ rows, cols: columns, cwd, shell }) { super(); + const baseEnv = Object.assign({}, process.env, { + LANG: app.getLocale().replace('-', '_'), + TERM: 'xterm-256color', + TERM_PROGRAM: productName, + TERM_PROGRAM_VERSION: version + }); + this.pty = spawn(shell || defaultShell, ['--login'], { columns, rows, cwd, - env: Object.assign({}, process.env, { - LANG: app.getLocale(), - TERM: 'xterm-256color', - TERM_PROGRAM: productName, - TERM_PROGRAM_VERSION: version - }) + env: getDecoratedEnv(baseEnv) }); this.pty.stdout.on('data', (data) => {