feat(plugin-infra): support worker thread in server side (#3462)

This commit is contained in:
Alex Yang
2023-07-29 13:57:23 -07:00
committed by GitHub
parent 77dab70ff7
commit ac59e28fcd
13 changed files with 152 additions and 70 deletions

View File

@@ -1,8 +1,8 @@
import { app, Menu } from 'electron';
import { isMacOS } from '../../shared/utils';
import { revealLogFile } from '../logger';
import { checkForUpdates } from '../updater';
import { isMacOS } from '../utils';
import { applicationMenuSubjects } from './subject';
// Unique id for menuitems

View File

@@ -15,8 +15,8 @@ import {
type WebContents,
} from 'electron';
import { MessageEventChannel } from '../shared/utils';
import { logger } from './logger';
import { MessageEventChannel } from './utils';
const HELPER_PROCESS_PATH = path.join(__dirname, './helper.js');

View File

@@ -2,6 +2,7 @@ import { shell } from 'electron';
import log from 'electron-log';
export const logger = log.scope('main');
export const pluginLogger = log.scope('plugin');
log.initialize();
export function getLogFilePath() {

View File

@@ -4,10 +4,10 @@ import { BrowserWindow, nativeTheme } from 'electron';
import electronWindowState from 'electron-window-state';
import { join } from 'path';
import { isMacOS, isWindows } from '../shared/utils';
import { getExposedMeta } from './exposed';
import { ensureHelperProcess } from './helper-process';
import { logger } from './logger';
import { isMacOS, isWindows } from './utils';
const IS_DEV: boolean =
process.env.NODE_ENV === 'development' && !process.env.CI;
@@ -114,6 +114,7 @@ async function createWindow() {
// singleton
let browserWindow: Electron.BrowserWindow | undefined;
/**
* Restore existing BrowserWindow or Create new BrowserWindow
*/

View File

@@ -1,7 +1,14 @@
import { join, resolve } from 'node:path';
import { Worker } from 'node:worker_threads';
import { logger } from '@affine/electron/main/logger';
import { logger, pluginLogger } from '@affine/electron/main/logger';
import { AsyncCall } from 'async-call-rpc';
import { ipcMain } from 'electron';
import { readFile } from 'fs/promises';
import { MessageEventChannel } from '../shared/utils';
const builtInPlugins = ['bookmark'];
declare global {
// fixme(himself65):
@@ -10,26 +17,41 @@ declare global {
var asyncCall: Record<string, (...args: any) => PromiseLike<any>>;
}
export function registerPlugin() {
export async function registerPlugin() {
logger.info('import plugin manager');
globalThis.asyncCall = {};
const bookmarkPluginPath = join(
process.env.PLUGIN_DIR ?? resolve(__dirname, './plugins'),
'./bookmark/index.js'
const asyncCall = AsyncCall<
Record<string, (...args: any) => PromiseLike<any>>
>(
{
log: (...args: any[]) => {
pluginLogger.log(...args);
},
},
{
channel: new MessageEventChannel(
new Worker(resolve(__dirname, './worker.js'), {})
),
}
);
globalThis.asyncCall = asyncCall;
await Promise.all(
builtInPlugins.map(async plugin => {
const pluginPackageJsonPath = join(
process.env.PLUGIN_DIR ?? resolve(__dirname, './plugins'),
`./${plugin}/package.json`
);
logger.info(`${plugin} plugin path:`, pluginPackageJsonPath);
const packageJson = JSON.parse(
await readFile(pluginPackageJsonPath, 'utf-8')
);
console.log('packageJson', packageJson);
const serverCommand: string[] = packageJson.affinePlugin.serverCommand;
serverCommand.forEach(command => {
ipcMain.handle(command, async (_, ...args) => {
logger.info(`plugin ${plugin} called`);
return asyncCall[command](...args);
});
});
})
);
logger.info('bookmark plugin path:', bookmarkPluginPath);
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { entry } = require(bookmarkPluginPath);
entry({
registerCommand: (command: string, handler: (...args: any[]) => any) => {
logger.info('register plugin command', command);
ipcMain.handle(command, (event, ...args) => handler(...args));
globalThis.asyncCall[command] = handler;
},
registerCommands: (command: string) => {
ipcMain.removeHandler(command);
delete globalThis.asyncCall[command];
},
});
}

View File

@@ -1,7 +1,7 @@
import { app, BrowserWindow, nativeTheme } from 'electron';
import { isMacOS } from '../../shared/utils';
import type { NamespaceHandlers } from '../type';
import { isMacOS } from '../utils';
import { getGoogleOauthCode } from './google-auth';
export const uiHandlers = {

View File

@@ -2,8 +2,8 @@ import { app } from 'electron';
import { autoUpdater } from 'electron-updater';
import { z } from 'zod';
import { isMacOS } from '../../shared/utils';
import { logger } from '../logger';
import { isMacOS } from '../utils';
import { updaterSubjects } from './event';
export const ReleaseTypeSchema = z.enum([

View File

@@ -1,40 +0,0 @@
import type { EventBasedChannel } from 'async-call-rpc';
export function getTime() {
return new Date().getTime();
}
export const isMacOS = () => {
return process.platform === 'darwin';
};
export const isWindows = () => {
return process.platform === 'win32';
};
interface MessagePortLike {
postMessage: (data: unknown) => void;
addListener: (event: 'message', listener: (...args: any[]) => void) => void;
removeListener: (
event: 'message',
listener: (...args: any[]) => void
) => void;
}
export class MessageEventChannel implements EventBasedChannel {
constructor(private worker: MessagePortLike) {}
on(listener: (data: unknown) => void) {
const f = (data: unknown) => {
listener(data);
};
this.worker.addListener('message', f);
return () => {
this.worker.removeListener('message', f);
};
}
send(data: unknown) {
this.worker.postMessage(data);
}
}