Skip to content
This repository has been archived by the owner on Jul 25, 2022. It is now read-only.

Commit

Permalink
Merge pull request #66 from squidjs/plugins
Browse files Browse the repository at this point in the history
Plugin system
  • Loading branch information
QuiiBz authored Mar 16, 2021
2 parents c09b606 + 15e2278 commit 2665e07
Show file tree
Hide file tree
Showing 16 changed files with 401 additions and 8 deletions.
24 changes: 22 additions & 2 deletions common/config/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import { defaultConfig } from '@common/config/defaultConfig';
import watch from 'node-watch';
import { FontWeight } from 'xterm';
import { UndefinedObject } from '@common/types/types';
import { IShortcut } from '@common/shortcuts/shortcuts';
import { IShortcut } from '@common/config/shortcuts';
import { callTrigger } from '@common/plugins/plugins';
import { getProcessTrigger } from '@common/plugins/hooks';

export default class Config {

Expand Down Expand Up @@ -85,7 +87,10 @@ export default class Config {
private readFile(): IConfig {

const data = fs.readFileSync(this.CONFIG);
const config: IConfig = JSON.parse(data.toString());
let config: IConfig = JSON.parse(data.toString());

const { param } = callTrigger('hookConfig', getProcessTrigger(config));
config = param;

this.config = config;

Expand All @@ -112,6 +117,17 @@ export default class Config {
});
}

/**
* Read the config file synchronously without hooking.
*
* @returns A IConfig
*/
public getHooklessConfig(): IConfig {

const data = fs.readFileSync(this.CONFIG);
return JSON.parse(data.toString());
}

/**
* Get the singleton instance of this Config object.
*/
Expand Down Expand Up @@ -174,6 +190,10 @@ export interface IConfig {
* The url to use for the cloud.
*/
cloudUrl: string;
/**
* The list of names of enabled plugin and theme.
*/
plugins: string[];
}

export interface ITheme {
Expand Down
1 change: 1 addition & 0 deletions common/config/defaultConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,4 +163,5 @@ export const defaultConfig: IConfig = {
localSSHHosts,
// TODO
cloudUrl: '',
plugins: [],
}
34 changes: 34 additions & 0 deletions common/plugins/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { BrowserWindow, BrowserWindowConstructorOptions, app } from 'electron';
import { IConfig } from '@common/config/Config';
import { isMainProcess, Process } from '@common/utils/utils';
import { Provider } from '@common/plugins/providers';
import { INotification } from '@app/notifications/notification';

// The list of availables parameters for the triggers
export type TriggerParams =
BrowserWindowConstructorOptions |
ProcessTriggerParam<IConfig> |
BrowserWindow |
typeof app |
Provider<INotification>;


// A helper type to specify the parameter for a trigger which can
// be called in both main and renderer process.
export type ProcessTriggerParam<P> = {

param: P;
process: Process;
}

/**
* Get a process trigger param with the given param, and automaticaly
* fill the process.
*
* @param param - The param to use
*/
export const getProcessTrigger = <P>(param: P): ProcessTriggerParam<P> => ({

param,
process: isMainProcess ? 'main' : 'renderer',
});
26 changes: 26 additions & 0 deletions common/plugins/local/material-theme/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "squid-material-theme",
"version": "1.0.0",
"description": "Material theme for Squid terminal.",
"author": {
"email": "[email protected]",
"name": "QuiiBz",
"url": "https://github.com/QuiiBz"
},
"license": "MIT",
"repository": "https://github.com/squidjs/terminal",
"keywords": [
"squid-theme",
"theme",
"shell",
"terminal",
"squid"
],
"scripts": {
"dist": "tsc"
},
"devDependencies": {
"squid-plugins": "^0.0.1",
"typescript": "^4.2.3"
}
}
43 changes: 43 additions & 0 deletions common/plugins/local/material-theme/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { SquidPlugin } from 'squid-plugins/src';

const MaterialTheme: SquidPlugin = {

hookConfig: (options) => {

return {
...options,
param: {
...options.param,
theme: {
...options.param.theme,
background: '#263238',
border: '#2E3C43',
text: '#425B67',
textHover: '#B0BEC5',
foreground: '#B0BEC5',
cursor: '#009688',
cursorAccent: '#1E272C',
selection: '#546E7A',
black: '#1E272C',
red: '#f78c6c',
green: '#c3e88d',
yellow: '#ffcb6b',
blue: '#82aaff',
magenta: '#c792ea',
cyan: '#89ddff',
white: '#eeffff',
brightBlack: '#1E272C',
brightRed: '#f78c6c',
brightGreen: '#c3e88d',
brightYellow: '#ffcb6b',
brightBlue: '#82aaff',
brightMagenta: '#c792ea',
brightCyan: '#89ddff',
brightWhite: '#eeffff',
},
},
};
},
}

export default MaterialTheme;
9 changes: 9 additions & 0 deletions common/plugins/local/material-theme/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"outDir": "./dist",
"skipLibCheck": true
},
"include": ["./src/**/*"]
}
13 changes: 13 additions & 0 deletions common/plugins/local/material-theme/yarn.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


squid-plugins@^0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/squid-plugins/-/squid-plugins-0.0.1.tgz#8250105ba82a0890e333c7403f78596394741cba"
integrity sha512-rgX37w5dql4yRp5GU45G8WIgTuqOnBxpeFNqlPLnreUKUEly/rpYQ+1nSeYPYYzZtzO4SJBQDeljC/61jh4GPw==

typescript@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.3.tgz#39062d8019912d43726298f09493d598048c1ce3"
integrity sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==
22 changes: 22 additions & 0 deletions common/plugins/package/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "squid-plugins",
"version": "0.0.1",
"description": "Package to create plugins and themes for Squid.",
"author": {
"email": "[email protected]",
"name": "QuiiBz",
"url": "https://github.com/QuiiBz"
},
"license": "MIT",
"repository": "https://github.com/squidjs/terminal",
"keywords": [
"plugin",
"theme",
"shell",
"terminal",
"squid"
],
"devDependencies": {
"typescript": "^4.2.3"
}
}
73 changes: 73 additions & 0 deletions common/plugins/package/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { BrowserWindow, BrowserWindowConstructorOptions, app } from 'electron';
import { IConfig } from '@common/config/Config';
import { ProcessTriggerParam } from '@common/plugins/hooks';
import { Provider } from '@common/plugins/providers';
import { INotification } from '@app/notifications/notification';

export interface SquidPlugin {

/**
* Event when the plugin load.
*
* Process: main
*/
onLoad?: () => void;

/**
* Event when the app has loaded.
*
* Process: main
*
* @param options - The electron app
*/
onAppLoaded?: (options: typeof app) => void;

/**
* Event when the app close.
*
* Process: main
*/
onAppClose?: () => void;

/**
* Event called when the window has finished building and has
* been shown after ready-to-show event.
*
* Process: main
*
* @param options - The browser window
*/
onWindowShow?: (options: BrowserWindow) => void;

/**
* Provide a notifications provider to add a notification. Called
* at the startup of the app.
*
* Process: renderer
*
* @param options - The notification provider
*/
provideNotifications?: (options: Provider<INotification>) => void;

/**
* Hook the window options. Called when the browser window is
* building.
*
* Process: main
*
* @param options - The browser window options
* @returns The hooked browser window options
*/
hookWindowOptions?: (options: BrowserWindowConstructorOptions) => BrowserWindowConstructorOptions;

/**
* Hook the config. Called when the config is loaded for the
* first time, and when the config change.
*
* Process: both
*
* @param options - The base config
* @returns The hooked config
*/
hookConfig?: (options: ProcessTriggerParam<IConfig>) => ProcessTriggerParam<IConfig>;
}
8 changes: 8 additions & 0 deletions common/plugins/package/yarn.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


typescript@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.3.tgz#39062d8019912d43726298f09493d598048c1ce3"
integrity sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==
105 changes: 105 additions & 0 deletions common/plugins/plugins.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { SquidPlugin } from '@common/plugins/package/src';
import { isDev, isMainProcess } from '../utils/utils';
import { TriggerParams } from '@common/plugins/hooks';
import electron from 'electron';
import path from 'path';
import fs from 'fs';
import Config from '@common/config/Config';

const PLUGINS_FOLDER = isDev ?
path.join(__dirname, 'local') :
path.join((electron.app || electron.remote.app).getPath('home'), '.squid');

// Keep track of is the plugins has been loaded in the current process
let pluginsLoaded = false;
let plugins: SquidPlugin[] = [];

/**
* Load a single plugin.
*
* @param pluginDir - The path to the plugin
* @returns The loaded plugin
*/
const loadPlugin = (pluginDir: string): SquidPlugin => {

let plugin: SquidPlugin;

if(isDev)
// eslint-disable-next-line @typescript-eslint/no-var-requires
plugin = require('./local/' + pluginDir + '/dist/index').default;
else
plugin = __non_webpack_require__(path.join(PLUGINS_FOLDER, pluginDir, 'dist', 'index'));

if(isMainProcess)
callPluginTrigger(plugin, 'onLoad');

return plugin;
}

/**
* Load all the plugins. Can be used to reload the plugin.
*/
const loadPlugins = () => {

const { plugins: enabledPlugins } = Config.getInstance().getHooklessConfig();

// Reset the plugins list
plugins = [];

if(!fs.existsSync(PLUGINS_FOLDER))
fs.mkdirSync(PLUGINS_FOLDER);

const pluginsDir = fs.readdirSync(PLUGINS_FOLDER);

pluginsDir
.filter((pluginDir) => enabledPlugins.includes(pluginDir))
.forEach((pluginDir) => {

plugins.push(loadPlugin(pluginDir));
});

pluginsLoaded = true;
}

/**
* Call a trigger for the given plugin, and use optional parameter which
* is a HookParam.
*
* @param plugin - The plugin to call the trigger on
* @param trigger - The trigger to call
* @param param - An optional parameter
* @returns The modifier parameter or undefined if no parameter was provided
*/
const callPluginTrigger = <T extends TriggerParams>(plugin: SquidPlugin, trigger: keyof SquidPlugin, param?: T): T => {

if(trigger in plugin)
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return param ? plugin[trigger](param) : plugin[trigger]();

return param as T;
}

/**
* Call a trigger for all the plugins, and and optional parameter which
* is a HookParam. We cache the result of each plugin's trigger and return
* it after trigger all the plugins.
*
* @param trigger - The trigger to call
* @param param - An optional parameter
* @returns The modifier parameter or undefined if no parameter was provided
*/
export const callTrigger = <T extends TriggerParams>(trigger: keyof SquidPlugin, param?: T): T => {

if(!pluginsLoaded || trigger === 'hookConfig')
loadPlugins();

let cache: T | undefined = param;

plugins.forEach((plugin) => {

cache = callPluginTrigger(plugin, trigger, cache ? cache : param);
});

return cache as T;
}
Loading

0 comments on commit 2665e07

Please sign in to comment.