From 1e7df65f575bea50c5e2dc3b1924dccda28fb732 Mon Sep 17 00:00:00 2001 From: rhysd Date: Fri, 21 Apr 2017 16:50:14 +0900 Subject: [PATCH] basic plugin implementation --- README.md | 90 +++++++++++++++++++++++++++++++++++++++++++++ main/config.ts | 2 + renderer/index.ts | 4 ++ renderer/plugins.ts | 29 +++++++++++++++ 4 files changed, 125 insertions(+) create mode 100644 renderer/plugins.ts diff --git a/README.md b/README.md index e77e10e..ba4e4cb 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ Features: - [x] Customizable (and pluggable) shortcut keybinds - [x] Multi-account (switching among accounts) - [x] User CSS +- [x] Plugin architecture based on Node.js and Electron APIs Mastodon is an open source project. So if you want to make a new UI, you can just fork the project, implement your favorite UI and host it on your place. Then you can participate Mastodon networks from it. @@ -256,6 +257,93 @@ body { } ``` +## Preload Plugin + +You can make a Node.js package which is preloaded before loading inner mastodon page. + +Preload plugin is enabled if `chromium_sandbox` option is set to `false`. Please read above +configuration section before using any plugin. + +### How to make + +Create `node_modules` directory in your application directory at first. And then, please make +`mstdn-preload-hello` directory. It consists a node package. + +Package name must start with `mastdn-preload-`. + +Make `package.json` manually or by `$ npm init` in the directory. + +```json +{ + "name": "mstdn-preload-hello", + "version": "0.0.0", + "description": "sample of preload plugin of Mstdn.app", + "main": "index.js", + "author": "Your name", + "license": "Some license" +} +``` + +Finally make `index.js` as below: + +```javascript +module.exports = (config, account) => { + console.log('Hello from plugin!', config, account); +} +``` + +Package must export one function which receives two parameters `config` and `account`. + +Note that you can use below APIs in the script. + +- [Node.js standard libraries][] via `require` (e.g. `require('fs')`) +- Dependant node packages listed in `package.json` of the plugin +- [Electron Renderer APIs][Electron APIs] +- All Web APIs on browser (including DOM API) + +#### !!! Security Notice !!! + +Do not leak Node.js stuff to global namespace like below. + +```javascript +// Never do things like this! +window.my_require = require; +``` + +### How to use + +If you didn't try above 'How to make' section, please install plugin package at first. +Below will install 'hello' plugin to `{app directory}/node_modules/mstdn-preload-hello`. + +``` +$ cd {Your application directory} +$ npm install mstdn-preload-hello +``` + +And then write what plugin should be loaded to `"preload"` section of your `config.json`. +`"hello"` should be added to the list. + +```json +{ + ... + + "preload" [ + "hello" + ], + + ... +} +``` + +Finally open Mstdn.app and see DevTools via [Menu] -> [View] -> [Developper Tools] -> console. +If window is too small to see DevTools, please make window size bigger. + +In console, the message 'Hello from plugin!', config information and account information should be output. + +### List of Plugins + +To be made + ## Contact to the Author Please feel free to make an issue on GitHub or mention on Mastodon/Twitter. @@ -269,3 +357,5 @@ Please feel free to make an issue on GitHub or mention on Mastodon/Twitter. [Electron]: electron.atom.io [sandbox doc]: https://github.com/electron/electron/blob/master/docs/api/sandbox-option.md [npm version badge]: https://badge.fury.io/js/mstdn.svg +[Node.js standard libraries]: https://nodejs.org/api/ +[Electron APIs]: https://electron.atom.io/docs/api/ diff --git a/main/config.ts b/main/config.ts index 3a942d5..5a7b3c5 100644 --- a/main/config.ts +++ b/main/config.ts @@ -20,6 +20,7 @@ export interface Config { chromium_sandbox: boolean; __DATA_DIR?: string; keymaps: {[key: string]: string}; + preload: string[]; } function makeDefaultConfig(): Config { @@ -51,6 +52,7 @@ function makeDefaultConfig(): Config { 5: '/web/timelines/public', 6: '/web/getting-started' }, + preload: [], }; } diff --git a/renderer/index.ts b/renderer/index.ts index d227b1b..9b2d649 100644 --- a/renderer/index.ts +++ b/renderer/index.ts @@ -1,8 +1,12 @@ import {Config, Account} from '../main/config'; import * as Ipc from './ipc'; import setupKeymaps from './key_handler'; +import loadPlugins from './plugins'; +import log from './log'; Ipc.on('mstdn:config', (c: Config, a: Account) => { + const plugins = loadPlugins(c, a); + log.info('Loaded plugins:', plugins); setupKeymaps(c, a); document.title = `${document.title} @${a.name}@${a.host}`; }); diff --git a/renderer/plugins.ts b/renderer/plugins.ts new file mode 100644 index 0000000..8ff899e --- /dev/null +++ b/renderer/plugins.ts @@ -0,0 +1,29 @@ +import * as path from 'path'; +import {Config, Account} from '../main/config'; +import r from './require'; +import log from './log'; + +type Plugin = (c: Config, a: Account) => void; +interface Plugins { + [module_path: string]: Plugin; +} + +export default function loadPlugins(config: Config, account: Account): Plugins { + const ret = {} as Plugins; + if (config.chromium_sandbox) { + log.info('Chromium sandbox is enabled. Preload plugin is disabled.'); + return ret; + } + const dir_base = path.join(config.__DATA_DIR!, 'node_modules'); + for (const plugin of config.preload || []) { + const plugin_path = path.join(dir_base, `mstdn-preload-${plugin}`); + try { + const preloadFunc = r(plugin_path) as Plugin; + preloadFunc(config, account); + ret[plugin_path] = preloadFunc; + } catch (e) { + log.error(`Failed to load plugin ${plugin_path}:`, e); + } + } + return ret; +}