2021-03-28 12:13:49 -08:00
|
|
|
// eslint-disable-next-line eslint-comments/disable-enable-pair
|
|
|
|
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
2016-09-21 06:27:11 -08:00
|
|
|
import {remote} from 'electron';
|
2019-10-25 04:34:52 -08:00
|
|
|
// TODO: Should be updates to new async API https://medium.com/@nornagon/electrons-remote-module-considered-harmful-70d69500f31
|
|
|
|
|
|
2019-11-11 06:21:42 -09:00
|
|
|
import {connect as reduxConnect, Options} from 'react-redux';
|
2017-11-13 06:02:49 -09:00
|
|
|
import {basename} from 'path';
|
2016-07-13 12:44:24 -08:00
|
|
|
|
2017-05-27 11:37:41 -08:00
|
|
|
// patching Module._load
|
2017-10-21 11:56:41 -08:00
|
|
|
// so plugins can `require` them without needing their own version
|
2020-07-13 05:05:34 -08:00
|
|
|
// https://github.com/vercel/hyper/issues/619
|
2018-03-17 04:51:36 -08:00
|
|
|
import React, {PureComponent} from 'react';
|
2017-05-27 11:37:41 -08:00
|
|
|
import ReactDOM from 'react-dom';
|
2016-07-13 12:44:24 -08:00
|
|
|
import Notification from '../components/notification';
|
|
|
|
|
import notify from './notify';
|
2020-03-18 07:26:46 -08:00
|
|
|
import {
|
|
|
|
|
hyperPlugin,
|
|
|
|
|
IUiReducer,
|
|
|
|
|
ISessionReducer,
|
|
|
|
|
ITermGroupReducer,
|
|
|
|
|
HyperState,
|
|
|
|
|
HyperDispatch,
|
|
|
|
|
TabProps,
|
|
|
|
|
TabsProps,
|
|
|
|
|
TermGroupOwnProps,
|
|
|
|
|
TermProps,
|
|
|
|
|
Assignable
|
|
|
|
|
} from '../hyper';
|
2020-03-07 05:38:46 -09:00
|
|
|
import {Middleware} from 'redux';
|
|
|
|
|
import {ObjectTypedKeys} from './object';
|
2016-07-13 12:44:24 -08:00
|
|
|
|
2019-10-19 08:59:56 -08:00
|
|
|
// remote interface to `../plugins`
|
|
|
|
|
const plugins = remote.require('./plugins') as typeof import('../../app/plugins');
|
|
|
|
|
|
|
|
|
|
// `require`d modules
|
2021-03-28 11:54:27 -08:00
|
|
|
let modules: hyperPlugin[];
|
2019-10-19 08:59:56 -08:00
|
|
|
|
|
|
|
|
// cache of decorated components
|
2020-03-07 05:38:46 -09:00
|
|
|
let decorated: Record<string, React.ComponentClass<any>> = {};
|
2019-10-19 08:59:56 -08:00
|
|
|
|
|
|
|
|
// various caches extracted of the plugin methods
|
|
|
|
|
let connectors: {
|
|
|
|
|
Terms: {state: any[]; dispatch: any[]};
|
|
|
|
|
Header: {state: any[]; dispatch: any[]};
|
|
|
|
|
Hyper: {state: any[]; dispatch: any[]};
|
|
|
|
|
Notifications: {state: any[]; dispatch: any[]};
|
|
|
|
|
};
|
2020-03-07 05:38:46 -09:00
|
|
|
let middlewares: Middleware[];
|
2019-10-19 08:59:56 -08:00
|
|
|
let uiReducers: IUiReducer[];
|
|
|
|
|
let sessionsReducers: ISessionReducer[];
|
|
|
|
|
let termGroupsReducers: ITermGroupReducer[];
|
|
|
|
|
let tabPropsDecorators: any[];
|
|
|
|
|
let tabsPropsDecorators: any[];
|
|
|
|
|
let termPropsDecorators: any[];
|
|
|
|
|
let termGroupPropsDecorators: any[];
|
|
|
|
|
let propsDecorators: {
|
|
|
|
|
getTermProps: any[];
|
|
|
|
|
getTabProps: any[];
|
|
|
|
|
getTabsProps: any[];
|
|
|
|
|
getTermGroupProps: any[];
|
|
|
|
|
};
|
|
|
|
|
let reducersDecorators: {
|
|
|
|
|
reduceUI: IUiReducer[];
|
|
|
|
|
reduceSessions: ISessionReducer[];
|
|
|
|
|
reduceTermGroups: ITermGroupReducer[];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// expose decorated component instance to the higher-order components
|
2020-05-28 20:00:52 -08:00
|
|
|
function exposeDecorated<P extends Record<string, any>>(
|
|
|
|
|
Component_: React.ComponentType<P>
|
2020-07-13 05:05:34 -08:00
|
|
|
): React.ComponentClass<P, unknown> {
|
2020-03-07 05:38:46 -09:00
|
|
|
return class DecoratedComponent extends React.Component<P> {
|
|
|
|
|
constructor(props: P, context: any) {
|
2019-10-19 08:59:56 -08:00
|
|
|
super(props, context);
|
|
|
|
|
}
|
2019-11-25 07:16:00 -09:00
|
|
|
onRef = (decorated_: any) => {
|
2019-10-19 08:59:56 -08:00
|
|
|
if (this.props.onDecorated) {
|
|
|
|
|
try {
|
2021-03-28 11:54:27 -08:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
2019-10-19 08:59:56 -08:00
|
|
|
this.props.onDecorated(decorated_);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
notify('Plugin error', `Error occurred. Check Developer Tools for details`, {error: e});
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-11-25 07:16:00 -09:00
|
|
|
};
|
2019-10-19 08:59:56 -08:00
|
|
|
render() {
|
|
|
|
|
return React.createElement(Component_, Object.assign({}, this.props, {ref: this.onRef}));
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-16 06:32:04 -08:00
|
|
|
function getDecorated<P>(parent: React.ComponentType<P>, name: string): React.ComponentClass<P> {
|
2019-10-19 08:59:56 -08:00
|
|
|
if (!decorated[name]) {
|
|
|
|
|
let class_ = exposeDecorated(parent);
|
|
|
|
|
(class_ as any).displayName = `_exposeDecorated(${name})`;
|
|
|
|
|
|
|
|
|
|
modules.forEach((mod: any) => {
|
|
|
|
|
const method = 'decorate' + name;
|
2021-03-28 11:54:27 -08:00
|
|
|
const fn: Function & {_pluginName: string} = mod[method];
|
2019-10-19 08:59:56 -08:00
|
|
|
|
|
|
|
|
if (fn) {
|
|
|
|
|
let class__;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
class__ = fn(class_, {React, PureComponent, Notification, notify});
|
|
|
|
|
class__.displayName = `${fn._pluginName}(${name})`;
|
|
|
|
|
} catch (err) {
|
|
|
|
|
notify(
|
|
|
|
|
'Plugin error',
|
|
|
|
|
`${fn._pluginName}: Error occurred in \`${method}\`. Check Developer Tools for details`,
|
|
|
|
|
{error: err}
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!class__ || typeof class__.prototype.render !== 'function') {
|
|
|
|
|
notify(
|
|
|
|
|
'Plugin error',
|
|
|
|
|
`${fn._pluginName}: Invalid return value of \`${method}\`. No \`render\` method found. Please return a \`React.Component\`.`
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class_ = class__;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
decorated[name] = class_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return decorated[name];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// for each component, we return a higher-order component
|
|
|
|
|
// that wraps with the higher-order components
|
|
|
|
|
// exposed by plugins
|
2020-03-07 05:38:46 -09:00
|
|
|
export function decorate<P>(
|
2020-03-16 06:32:04 -08:00
|
|
|
Component_: React.ComponentType<P>,
|
2020-03-07 05:38:46 -09:00
|
|
|
name: string
|
|
|
|
|
): React.ComponentClass<P, {hasError: boolean}> {
|
|
|
|
|
return class DecoratedComponent extends React.Component<P, {hasError: boolean}> {
|
|
|
|
|
constructor(props: P) {
|
2019-10-19 08:59:56 -08:00
|
|
|
super(props);
|
|
|
|
|
this.state = {hasError: false};
|
|
|
|
|
}
|
|
|
|
|
componentDidCatch() {
|
|
|
|
|
this.setState({hasError: true});
|
|
|
|
|
// No need to detail this error because React print these information.
|
|
|
|
|
notify(
|
|
|
|
|
'Plugin error',
|
|
|
|
|
`Plugins decorating ${name} has been disabled because of a plugin crash. Check Developer Tools for details.`
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
render() {
|
|
|
|
|
const Sub = this.state.hasError ? Component_ : getDecorated(Component_, name);
|
|
|
|
|
return React.createElement(Sub, this.props);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-13 05:05:34 -08:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
2019-10-19 08:59:56 -08:00
|
|
|
const Module = require('module') as typeof import('module') & {_load: Function};
|
2016-09-21 06:27:11 -08:00
|
|
|
const originalLoad = Module._load;
|
2019-10-19 08:59:56 -08:00
|
|
|
Module._load = function _load(path: string) {
|
2018-05-15 05:58:36 -08:00
|
|
|
// PLEASE NOTE: Code changes here, also need to be changed in
|
|
|
|
|
// app/plugins.js
|
2016-11-19 11:18:07 -09:00
|
|
|
switch (path) {
|
|
|
|
|
case 'react':
|
2018-06-10 22:43:57 -08:00
|
|
|
console.warn('DEPRECATED: If your plugin requires `react`, it must bundle it as a dependency');
|
2016-11-19 11:18:07 -09:00
|
|
|
return React;
|
2017-05-27 11:37:41 -08:00
|
|
|
case 'react-dom':
|
2018-06-10 22:43:57 -08:00
|
|
|
console.warn('DEPRECATED: If your plugin requires `react-dom`, it must bundle it as a dependency');
|
2017-05-27 11:37:41 -08:00
|
|
|
return ReactDOM;
|
2016-11-19 11:18:07 -09:00
|
|
|
case 'hyper/component':
|
2018-06-10 22:43:57 -08:00
|
|
|
console.warn(
|
2018-10-13 06:35:51 -08:00
|
|
|
'DEPRECATED: If your plugin requires `hyper/component`, it must requires `react.PureComponent` instead and bundle `react` as a dependency'
|
2018-06-10 22:43:57 -08:00
|
|
|
);
|
2017-09-16 11:17:22 -08:00
|
|
|
return PureComponent;
|
2016-11-19 11:18:07 -09:00
|
|
|
case 'hyper/notify':
|
|
|
|
|
return notify;
|
|
|
|
|
case 'hyper/Notification':
|
|
|
|
|
return Notification;
|
2016-12-31 19:26:10 -09:00
|
|
|
case 'hyper/decorate':
|
|
|
|
|
return decorate;
|
2016-11-19 11:18:07 -09:00
|
|
|
default:
|
2019-10-19 08:59:56 -08:00
|
|
|
// eslint-disable-next-line prefer-rest-params
|
2016-11-19 11:18:07 -09:00
|
|
|
return originalLoad.apply(this, arguments);
|
2016-08-14 21:17:33 -08:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2016-07-13 12:44:24 -08:00
|
|
|
const clearModulesCache = () => {
|
2017-09-10 05:35:10 -08:00
|
|
|
// the fs locations where user plugins are stored
|
|
|
|
|
const {path, localPath} = plugins.getBasePaths();
|
|
|
|
|
|
2016-07-13 21:18:06 -08:00
|
|
|
// trigger unload hooks
|
2021-03-28 11:54:27 -08:00
|
|
|
modules.forEach((mod) => {
|
2016-09-21 06:27:11 -08:00
|
|
|
if (mod.onRendererUnload) {
|
2021-03-28 11:54:27 -08:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
2016-09-21 06:27:11 -08:00
|
|
|
mod.onRendererUnload(window);
|
|
|
|
|
}
|
2016-07-13 21:18:06 -08:00
|
|
|
});
|
|
|
|
|
|
2016-07-13 12:44:24 -08:00
|
|
|
// clear require cache
|
|
|
|
|
for (const entry in window.require.cache) {
|
|
|
|
|
if (entry.indexOf(path) === 0 || entry.indexOf(localPath) === 0) {
|
2016-10-10 02:26:47 -08:00
|
|
|
// `require` is webpacks', `window.require` is electron's
|
2016-07-13 12:44:24 -08:00
|
|
|
delete window.require.cache[entry];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2019-10-19 08:59:56 -08:00
|
|
|
const pathModule = window.require('path') as typeof import('path');
|
2017-06-19 13:02:53 -08:00
|
|
|
|
2019-10-19 08:59:56 -08:00
|
|
|
const getPluginName = (path: string) => pathModule.basename(path);
|
2017-06-19 13:02:53 -08:00
|
|
|
|
2019-10-19 08:59:56 -08:00
|
|
|
const getPluginVersion = (path: string): string | null => {
|
2017-06-19 13:02:53 -08:00
|
|
|
let version = null;
|
|
|
|
|
try {
|
2021-03-28 09:07:04 -08:00
|
|
|
version = window.require(pathModule.resolve(path, 'package.json')).version as string;
|
2017-06-19 13:02:53 -08:00
|
|
|
} catch (err) {
|
|
|
|
|
console.warn(`No package.json found in ${path}`);
|
|
|
|
|
}
|
|
|
|
|
return version;
|
|
|
|
|
};
|
2016-07-13 12:44:24 -08:00
|
|
|
|
|
|
|
|
const loadModules = () => {
|
|
|
|
|
console.log('(re)loading renderer plugins');
|
|
|
|
|
const paths = plugins.getPaths();
|
|
|
|
|
|
|
|
|
|
// initialize cache that we populate with extension methods
|
|
|
|
|
connectors = {
|
2016-09-21 06:27:11 -08:00
|
|
|
Terms: {state: [], dispatch: []},
|
|
|
|
|
Header: {state: [], dispatch: []},
|
2016-10-08 08:26:07 -08:00
|
|
|
Hyper: {state: [], dispatch: []},
|
2016-09-21 06:27:11 -08:00
|
|
|
Notifications: {state: [], dispatch: []}
|
2016-07-13 12:44:24 -08:00
|
|
|
};
|
|
|
|
|
uiReducers = [];
|
|
|
|
|
middlewares = [];
|
|
|
|
|
sessionsReducers = [];
|
2016-10-03 18:00:50 -08:00
|
|
|
termGroupsReducers = [];
|
2016-07-13 12:44:24 -08:00
|
|
|
tabPropsDecorators = [];
|
|
|
|
|
tabsPropsDecorators = [];
|
|
|
|
|
termPropsDecorators = [];
|
2016-10-03 18:00:50 -08:00
|
|
|
termGroupPropsDecorators = [];
|
2016-07-13 12:44:24 -08:00
|
|
|
|
2016-10-08 13:38:47 -08:00
|
|
|
propsDecorators = {
|
|
|
|
|
getTermProps: termPropsDecorators,
|
|
|
|
|
getTabProps: tabPropsDecorators,
|
|
|
|
|
getTabsProps: tabsPropsDecorators,
|
|
|
|
|
getTermGroupProps: termGroupPropsDecorators
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
reducersDecorators = {
|
|
|
|
|
reduceUI: uiReducers,
|
|
|
|
|
reduceSessions: sessionsReducers,
|
|
|
|
|
reduceTermGroups: termGroupsReducers
|
|
|
|
|
};
|
|
|
|
|
|
2019-10-19 08:59:56 -08:00
|
|
|
const loadedPlugins = plugins.getLoadedPluginVersions().map((plugin: any) => plugin.name);
|
2017-09-10 05:35:10 -08:00
|
|
|
modules = paths.plugins
|
|
|
|
|
.concat(paths.localPlugins)
|
2021-03-28 11:54:27 -08:00
|
|
|
.filter((plugin) => loadedPlugins.indexOf(basename(plugin)) !== -1)
|
|
|
|
|
.map((path) => {
|
2019-10-19 08:59:56 -08:00
|
|
|
let mod: hyperPlugin;
|
2016-07-29 12:40:46 -08:00
|
|
|
const pluginName = getPluginName(path);
|
2017-06-19 13:02:53 -08:00
|
|
|
const pluginVersion = getPluginVersion(path);
|
2016-07-29 12:40:46 -08:00
|
|
|
|
|
|
|
|
// window.require allows us to ensure this doesn't get
|
|
|
|
|
// in the way of our build
|
|
|
|
|
try {
|
2021-03-28 11:54:27 -08:00
|
|
|
mod = window.require(path);
|
2016-07-29 12:40:46 -08:00
|
|
|
} catch (err) {
|
2017-09-10 05:35:10 -08:00
|
|
|
notify(
|
|
|
|
|
'Plugin load error',
|
2018-05-02 00:10:44 -08:00
|
|
|
`"${pluginName}" failed to load in the renderer process. Check Developer Tools for details.`,
|
|
|
|
|
{error: err}
|
2017-09-10 05:35:10 -08:00
|
|
|
);
|
2016-09-21 06:27:11 -08:00
|
|
|
return undefined;
|
2016-07-29 12:40:46 -08:00
|
|
|
}
|
|
|
|
|
|
2020-03-25 02:15:08 -08:00
|
|
|
ObjectTypedKeys(mod).forEach((i) => {
|
2019-10-19 08:59:56 -08:00
|
|
|
if (Object.hasOwnProperty.call(mod, i)) {
|
2016-09-21 06:27:11 -08:00
|
|
|
mod[i]._pluginName = pluginName;
|
2017-06-19 13:02:53 -08:00
|
|
|
mod[i]._pluginVersion = pluginVersion;
|
2016-09-21 06:27:11 -08:00
|
|
|
}
|
2019-10-19 08:59:56 -08:00
|
|
|
});
|
2016-07-29 12:40:46 -08:00
|
|
|
|
2016-10-08 08:26:07 -08:00
|
|
|
// mapHyperTermState mapping for backwards compatibility with hyperterm
|
|
|
|
|
if (mod.mapHyperTermState) {
|
|
|
|
|
mod.mapHyperState = mod.mapHyperTermState;
|
|
|
|
|
console.error('mapHyperTermState is deprecated. Use mapHyperState instead.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// mapHyperTermDispatch mapping for backwards compatibility with hyperterm
|
|
|
|
|
if (mod.mapHyperTermDispatch) {
|
|
|
|
|
mod.mapHyperDispatch = mod.mapHyperTermDispatch;
|
|
|
|
|
console.error('mapHyperTermDispatch is deprecated. Use mapHyperDispatch instead.');
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-29 12:40:46 -08:00
|
|
|
if (mod.middleware) {
|
|
|
|
|
middlewares.push(mod.middleware);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mod.reduceUI) {
|
|
|
|
|
uiReducers.push(mod.reduceUI);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mod.reduceSessions) {
|
|
|
|
|
sessionsReducers.push(mod.reduceSessions);
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-08 13:38:47 -08:00
|
|
|
if (mod.reduceTermGroups) {
|
|
|
|
|
termGroupsReducers.push(mod.reduceTermGroups);
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-29 12:40:46 -08:00
|
|
|
if (mod.mapTermsState) {
|
|
|
|
|
connectors.Terms.state.push(mod.mapTermsState);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mod.mapTermsDispatch) {
|
|
|
|
|
connectors.Terms.dispatch.push(mod.mapTermsDispatch);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mod.mapHeaderState) {
|
|
|
|
|
connectors.Header.state.push(mod.mapHeaderState);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mod.mapHeaderDispatch) {
|
|
|
|
|
connectors.Header.dispatch.push(mod.mapHeaderDispatch);
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-08 08:26:07 -08:00
|
|
|
if (mod.mapHyperState) {
|
|
|
|
|
connectors.Hyper.state.push(mod.mapHyperState);
|
2016-07-29 12:40:46 -08:00
|
|
|
}
|
|
|
|
|
|
2016-10-08 08:26:07 -08:00
|
|
|
if (mod.mapHyperDispatch) {
|
|
|
|
|
connectors.Hyper.dispatch.push(mod.mapHyperDispatch);
|
2016-07-29 12:40:46 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mod.mapNotificationsState) {
|
|
|
|
|
connectors.Notifications.state.push(mod.mapNotificationsState);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mod.mapNotificationsDispatch) {
|
|
|
|
|
connectors.Notifications.dispatch.push(mod.mapNotificationsDispatch);
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-03 18:00:50 -08:00
|
|
|
if (mod.getTermGroupProps) {
|
|
|
|
|
termGroupPropsDecorators.push(mod.getTermGroupProps);
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-29 12:40:46 -08:00
|
|
|
if (mod.getTermProps) {
|
|
|
|
|
termPropsDecorators.push(mod.getTermProps);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mod.getTabProps) {
|
|
|
|
|
tabPropsDecorators.push(mod.getTabProps);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mod.getTabsProps) {
|
|
|
|
|
tabsPropsDecorators.push(mod.getTabsProps);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mod.onRendererWindow) {
|
2021-03-28 11:54:27 -08:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
2016-07-29 12:40:46 -08:00
|
|
|
mod.onRendererWindow(window);
|
|
|
|
|
}
|
2017-06-19 13:02:53 -08:00
|
|
|
console.log(`Plugin ${pluginName} (${pluginVersion}) loaded.`);
|
|
|
|
|
|
2016-07-29 12:40:46 -08:00
|
|
|
return mod;
|
|
|
|
|
})
|
2021-03-28 11:54:27 -08:00
|
|
|
.filter((mod): mod is hyperPlugin => Boolean(mod));
|
2017-09-10 01:35:39 -08:00
|
|
|
|
2020-04-27 05:32:08 -08:00
|
|
|
const deprecatedPlugins = plugins.getDeprecatedConfig();
|
2020-03-25 02:15:08 -08:00
|
|
|
Object.keys(deprecatedPlugins).forEach((name) => {
|
2017-09-10 05:35:10 -08:00
|
|
|
const {css} = deprecatedPlugins[name];
|
2020-04-27 05:32:08 -08:00
|
|
|
if (css.length > 0) {
|
2017-09-10 01:35:39 -08:00
|
|
|
console.warn(`Warning: "${name}" plugin uses some deprecated CSS classes (${css.join(', ')}).`);
|
|
|
|
|
}
|
|
|
|
|
});
|
2016-07-13 12:44:24 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// load modules for initial decoration
|
|
|
|
|
loadModules();
|
|
|
|
|
|
2016-09-21 06:27:11 -08:00
|
|
|
export function reload() {
|
2016-07-13 12:44:24 -08:00
|
|
|
clearModulesCache();
|
|
|
|
|
loadModules();
|
|
|
|
|
// trigger re-decoration when components
|
|
|
|
|
// get re-rendered
|
|
|
|
|
decorated = {};
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-19 08:59:56 -08:00
|
|
|
function getProps(name: keyof typeof propsDecorators, props: any, ...fnArgs: any[]) {
|
2016-10-08 13:38:47 -08:00
|
|
|
const decorators = propsDecorators[name];
|
2019-10-19 08:59:56 -08:00
|
|
|
let props_: typeof props;
|
2016-10-03 18:00:50 -08:00
|
|
|
|
2020-03-25 02:15:08 -08:00
|
|
|
decorators.forEach((fn) => {
|
2016-10-03 18:00:50 -08:00
|
|
|
let ret_;
|
|
|
|
|
|
|
|
|
|
if (!props_) {
|
|
|
|
|
props_ = Object.assign({}, props);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
2021-03-28 11:54:27 -08:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
2016-10-08 13:38:47 -08:00
|
|
|
ret_ = fn(...fnArgs, props_);
|
2016-10-03 18:00:50 -08:00
|
|
|
} catch (err) {
|
2018-05-02 00:10:44 -08:00
|
|
|
notify('Plugin error', `${fn._pluginName}: Error occurred in \`${name}\`. Check Developer Tools for details.`, {
|
|
|
|
|
error: err
|
|
|
|
|
});
|
2016-10-03 18:00:50 -08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!ret_ || typeof ret_ !== 'object') {
|
2016-10-08 13:38:47 -08:00
|
|
|
notify('Plugin error', `${fn._pluginName}: Invalid return value of \`${name}\` (object expected).`);
|
2016-10-03 18:00:50 -08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-08 13:38:47 -08:00
|
|
|
props_ = ret_;
|
2016-10-03 18:00:50 -08:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return props_ || props;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-18 07:26:46 -08:00
|
|
|
export function getTermGroupProps<T extends Assignable<TermGroupOwnProps, T>>(
|
|
|
|
|
uid: string,
|
|
|
|
|
parentProps: any,
|
|
|
|
|
props: T
|
|
|
|
|
): T {
|
2016-10-08 13:38:47 -08:00
|
|
|
return getProps('getTermGroupProps', props, uid, parentProps);
|
|
|
|
|
}
|
2016-07-13 12:44:24 -08:00
|
|
|
|
2020-03-18 07:26:46 -08:00
|
|
|
export function getTermProps<T extends Assignable<TermProps, T>>(uid: string, parentProps: any, props: T): T {
|
2016-10-08 13:38:47 -08:00
|
|
|
return getProps('getTermProps', props, uid, parentProps);
|
2016-07-13 12:44:24 -08:00
|
|
|
}
|
|
|
|
|
|
2020-03-18 07:26:46 -08:00
|
|
|
export function getTabsProps<T extends Assignable<TabsProps, T>>(parentProps: any, props: T): T {
|
2016-10-08 13:38:47 -08:00
|
|
|
return getProps('getTabsProps', props, parentProps);
|
2016-07-13 12:44:24 -08:00
|
|
|
}
|
|
|
|
|
|
2020-03-18 07:26:46 -08:00
|
|
|
export function getTabProps<T extends Assignable<TabProps, T>>(tab: any, parentProps: any, props: T): T {
|
2016-10-08 13:38:47 -08:00
|
|
|
return getProps('getTabProps', props, tab, parentProps);
|
2016-07-13 12:44:24 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// connects + decorates a class
|
|
|
|
|
// plugins can override mapToState, dispatchToProps
|
2016-07-17 13:05:37 -08:00
|
|
|
// and the class gets decorated (proxied)
|
2019-11-11 06:21:42 -09:00
|
|
|
export function connect<stateProps, dispatchProps>(
|
|
|
|
|
stateFn: (state: HyperState) => stateProps,
|
2020-03-07 05:38:46 -09:00
|
|
|
dispatchFn: (dispatch: HyperDispatch) => dispatchProps,
|
2019-11-11 06:21:42 -09:00
|
|
|
c: any,
|
|
|
|
|
d: Options = {}
|
|
|
|
|
) {
|
2019-10-19 08:59:56 -08:00
|
|
|
return (Class: any, name: keyof typeof connectors) => {
|
2019-11-11 06:21:42 -09:00
|
|
|
return reduxConnect<stateProps, dispatchProps, any, HyperState>(
|
2020-03-25 02:15:08 -08:00
|
|
|
(state) => {
|
2016-07-13 12:44:24 -08:00
|
|
|
let ret = stateFn(state);
|
2020-03-25 02:15:08 -08:00
|
|
|
connectors[name].state.forEach((fn) => {
|
2016-07-13 12:44:24 -08:00
|
|
|
let ret_;
|
|
|
|
|
|
|
|
|
|
try {
|
2021-03-28 11:54:27 -08:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
2016-07-13 12:44:24 -08:00
|
|
|
ret_ = fn(state, ret);
|
|
|
|
|
} catch (err) {
|
2017-09-10 05:35:10 -08:00
|
|
|
notify(
|
|
|
|
|
'Plugin error',
|
2018-05-02 00:10:44 -08:00
|
|
|
`${fn._pluginName}: Error occurred in \`map${name}State\`. Check Developer Tools for details.`,
|
|
|
|
|
{error: err}
|
2017-09-10 05:35:10 -08:00
|
|
|
);
|
2016-07-13 12:44:24 -08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-21 06:27:11 -08:00
|
|
|
if (!ret_ || typeof ret_ !== 'object') {
|
2016-07-13 12:44:24 -08:00
|
|
|
notify('Plugin error', `${fn._pluginName}: Invalid return value of \`map${name}State\` (object expected).`);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = ret_;
|
|
|
|
|
});
|
|
|
|
|
return ret;
|
|
|
|
|
},
|
2020-03-25 02:15:08 -08:00
|
|
|
(dispatch) => {
|
2016-08-06 01:17:09 -08:00
|
|
|
let ret = dispatchFn(dispatch);
|
2020-03-25 02:15:08 -08:00
|
|
|
connectors[name].dispatch.forEach((fn) => {
|
2016-08-06 01:17:09 -08:00
|
|
|
let ret_;
|
|
|
|
|
|
|
|
|
|
try {
|
2021-03-28 11:54:27 -08:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
2016-08-06 01:17:09 -08:00
|
|
|
ret_ = fn(dispatch, ret);
|
|
|
|
|
} catch (err) {
|
2017-09-10 05:35:10 -08:00
|
|
|
notify(
|
|
|
|
|
'Plugin error',
|
2018-05-02 00:10:44 -08:00
|
|
|
`${fn._pluginName}: Error occurred in \`map${name}Dispatch\`. Check Developer Tools for details.`,
|
|
|
|
|
{error: err}
|
2017-09-10 05:35:10 -08:00
|
|
|
);
|
2016-08-06 01:17:09 -08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-21 06:27:11 -08:00
|
|
|
if (!ret_ || typeof ret_ !== 'object') {
|
2017-09-10 05:35:10 -08:00
|
|
|
notify(
|
|
|
|
|
'Plugin error',
|
|
|
|
|
`${fn._pluginName}: Invalid return value of \`map${name}Dispatch\` (object expected).`
|
|
|
|
|
);
|
2016-08-06 01:17:09 -08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = ret_;
|
|
|
|
|
});
|
|
|
|
|
return ret;
|
|
|
|
|
},
|
2016-07-13 12:44:24 -08:00
|
|
|
c,
|
|
|
|
|
d
|
|
|
|
|
)(decorate(Class, name));
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-19 08:59:56 -08:00
|
|
|
const decorateReducer: {
|
|
|
|
|
(name: 'reduceUI', fn: IUiReducer): IUiReducer;
|
|
|
|
|
(name: 'reduceSessions', fn: ISessionReducer): ISessionReducer;
|
|
|
|
|
(name: 'reduceTermGroups', fn: ITermGroupReducer): ITermGroupReducer;
|
|
|
|
|
} = <T extends keyof typeof reducersDecorators>(name: T, fn: any) => {
|
2016-10-08 13:38:47 -08:00
|
|
|
const reducers = reducersDecorators[name];
|
2019-10-19 08:59:56 -08:00
|
|
|
return (state: any, action: any) => {
|
2021-03-28 11:54:27 -08:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
2016-10-03 18:00:50 -08:00
|
|
|
let state_ = fn(state, action);
|
|
|
|
|
|
2019-10-19 08:59:56 -08:00
|
|
|
reducers.forEach((pluginReducer: any) => {
|
2016-10-03 18:00:50 -08:00
|
|
|
let state__;
|
|
|
|
|
|
|
|
|
|
try {
|
2021-03-28 11:54:27 -08:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
2016-10-03 18:00:50 -08:00
|
|
|
state__ = pluginReducer(state_, action);
|
|
|
|
|
} catch (err) {
|
2018-05-02 00:10:44 -08:00
|
|
|
notify('Plugin error', `${fn._pluginName}: Error occurred in \`${name}\`. Check Developer Tools for details.`, {
|
|
|
|
|
error: err
|
|
|
|
|
});
|
2016-10-03 18:00:50 -08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!state__ || typeof state__ !== 'object') {
|
2016-10-08 13:38:47 -08:00
|
|
|
notify('Plugin error', `${fn._pluginName}: Invalid return value of \`${name}\`.`);
|
2016-10-03 18:00:50 -08:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
state_ = state__;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return state_;
|
|
|
|
|
};
|
2019-10-19 08:59:56 -08:00
|
|
|
};
|
2016-10-03 18:00:50 -08:00
|
|
|
|
2019-10-19 08:59:56 -08:00
|
|
|
export function decorateTermGroupsReducer(fn: ITermGroupReducer) {
|
2016-10-08 13:38:47 -08:00
|
|
|
return decorateReducer('reduceTermGroups', fn);
|
|
|
|
|
}
|
2016-07-13 12:44:24 -08:00
|
|
|
|
2019-10-19 08:59:56 -08:00
|
|
|
export function decorateUIReducer(fn: IUiReducer) {
|
2016-10-08 13:38:47 -08:00
|
|
|
return decorateReducer('reduceUI', fn);
|
2016-07-13 12:44:24 -08:00
|
|
|
}
|
|
|
|
|
|
2019-10-19 08:59:56 -08:00
|
|
|
export function decorateSessionsReducer(fn: ISessionReducer) {
|
2016-10-08 13:38:47 -08:00
|
|
|
return decorateReducer('reduceSessions', fn);
|
2016-07-13 12:44:24 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// redux middleware generator
|
2020-03-25 02:15:08 -08:00
|
|
|
export const middleware: Middleware = (store) => (next) => (action) => {
|
2020-01-02 08:49:57 -09:00
|
|
|
const nextMiddleware = (remaining: Middleware[]) => (action_: any) =>
|
2017-09-10 05:35:10 -08:00
|
|
|
remaining.length ? remaining[0](store)(nextMiddleware(remaining.slice(1)))(action_) : next(action_);
|
2016-07-13 12:44:24 -08:00
|
|
|
nextMiddleware(middlewares)(action);
|
|
|
|
|
};
|