Skip to content

Plugin(experimental)

info

  • Plug-in Center is used to extend the insufficiency of the software and provide more functions to the users.
  • This feature integrates the npm package management tool into the software.

1. Category

Plugin are currently categorized into System Plugin, Ui Plugin and Mix Plugin, and the differences between these plug-ins and their functions are described below.

  1. System plugin: there may be no UI interface, after installation, the plug-in will start their own back-end services, and provide ports for users to use.
  2. View plugin: will have a UI interface, used to interact with the user, does not have the ability to start services.
  3. Mix plugin: both UI interface and back-end services provided

2. System plugin

2.1. Tree

system-plugin-demo
├─ package.json
├─ index.js
├─ README.md

2.2. File info

2.2.1. package.json

json
{
  "name": "system-plugin-demo",
  "pluginName": "system demo",
  "version": "0.0.0",
  "description": "this is a system plugin demo",
  "logo": "https://xxxx/upload/xxxb13xxx.png",
  "author": "zyfun",
  "main": "index.js",
  "pluginType": "system"
}
fieldinforemark
namenpm package namerequired
pluginNameDisplay namerequired
versionversiondefault: 0.0.0
descriptionDescribe what this plugin doesdefault:
logologodefault:
authorauthordefault:
mainEntry file, Generally for index.jsrequired
pluginTypetype, enum: system,ui,mixsystem

2.2.2. index.js

Entry file

  • The development code must provide start and stop methods in the entry file, corresponding to the start and stop of the plugin, respectively.
  • Do not provide port 5173 [main program development port] | 9978 [main program backend interface].
js
··· custom code

export { start, stop }
js
··· custom code

module.exports { start, stop }

2.2.3. README.md

Some description of the project

md
# System plugin demo

## This is an unprecedented system plugin

## When launched, it will provide services on port 5777.

...

2.3. Construct

Startup logic

js
import workerpool from 'workerpool';
import { resolve } from 'path';

const manageModule = async (entryBasePath: string, modulePath: string, method: 'stop' | 'start'): Promise<boolean> => {
  const workerpool = await import('workerpool');

  const hasLifecycle = (obj: unknown, method: 'start' | 'stop'): obj is Record<typeof method, () => unknown> => {
    return typeof obj === 'object' && obj !== null && typeof (obj as any)[method] === 'function';
  };

  try {
    process.chdir(entryBasePath);

    let rawMod;
    if (method === 'start') {
      rawMod = await import(modulePath);
      globalThis.entryModule = rawMod;
    } else if (method === 'stop') {
      rawMod = globalThis.entryModule;
    }

    if (!rawMod) {
      console.warn(`Module not found`);
      return false;
    }

    const cand1 = rawMod?.default ?? undefined;
    const cand2 = (() => {
      const { default: _default, ...rest } = rawMod;
      return Object.keys(rest).length > 0 ? rest : undefined;
    })();

    let mod = {};
    if (hasLifecycle(cand1, method)) mod = cand1;
    if (hasLifecycle(cand2, method)) mod = cand2;

    if (mod && typeof mod === 'object' && Object.keys(mod).length === 0) {
      console.warn(`Module not implement ${method} method`);
      return false;
    }

    const fn = mod?.[method];
    if (typeof fn === 'function') {
      const resp = await fn();
      return !!resp; // boolean
    }

    return false;
  } catch (error) {
    console.error(`Module execution failed for ${method}`, (error as Error).message);
    return false;
  }
};

const pluginInfo = {}; // plugin info
const entryBasePath = fileURLToPath(dirname(pluginInfo.main));

const pool = workerpool.pool();

// start
await pool.exec(manageModule, [entryBasePath, pluginInfo.main, 'start']);

// stop
await pool.exec(manageModule, [entryBasePath, pluginInfo.main, 'stop']);
pool.terminate();

3. Ui plugin

3.1. Tree

ui-plugin-demo
├─ package.json
├─ index.html

3.2. File info

3.2.1. package.json

json
{
  "name": "ui-plugin-demo",
  "pluginName": "ui demo",
  "version": "0.0.0",
  "description": "this is a ui plugin demo",
  "logo": "https://xxxx/upload/xxxb13xxx.png",
  "author": "zyfun",
  "web": "index.html",
  "pluginType": "ui"
}
fieldinforemark
namenpm package namerequired
pluginNameDisplay namerequired
versionversiondefault: 0.0.0
descriptionDescribe what this plugin doesdefault:
logologodefault:
authorauthordefault:
webEntry file, Generally for index.htmlrequired
pluginTypetype, enum: system,ui,mixui

3.2.2. index.html

Entry file

  • The operating environment is browser-only (all the limitations of a browser are present).
  • Need back-end request to send the request header can go to the interface http://127.0.0.1:9978/api/v1/system/req.
  • No cross-domain restrictions, the bottom has been tampered with
  • Local static resources with relative path
3.2.2.1. Pure html development case
html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="./xxx.css">
    <title>Html App</title>
  </head>
  <body>
    <div id="app">
      ...
    </div>
    <script src="./main.js"></script>
  </body>
</html>
js
...

console.log('hello world');
3.2.2.2. Engineering development case
js
import { defineConfig } from 'vite';
import path from 'path';

export default defineConfig({
  base: './', // 设置基础路径为当前目录, 重要[否则加载路径不对]
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src/assets') // 自己设置别名[可选]
    }
  }
});
js
{
  ...
  "scripts": {
    "dev": "vite",
    "build": "vite build"
  },
  "devDependencies": {
    "vite": "^7.3.1",
  }
}
html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vite App</title>
  </head>
  <body>
    <div id="app">
      ...
    </div>
    <script type="module" src="./src/main.js"></script>
  </body>
</html>
js
...

console.log('hello world');
  1. build
bash
npm run build
  1. Collate

Create a new package.json file in the dist directory, and configure the plugin information, The dist directory is the result of the plugin.

3.3. Construct

3.3.1. 选型

js
...
plugins: [
  vue({
    template: {
      compilerOptions: {
        isCustomElement: (tag) => tag === 'webview'
      },
    },
  })
]
js
new BrowserWindow({
  ...
  webPreferences: {
    ...
    webviewTag: true
  },
});
js
<webview
  src="file:///xxxx"
  disablewebsecurity
  allowpopups
  nodeIntegration
/>

Released under the AGPL-3.0 License