Skip to content

Commit

Permalink
feat: split functon register and emmiter at main process (#23)
Browse files Browse the repository at this point in the history
  • Loading branch information
mato533 authored Oct 20, 2024
1 parent aba98df commit 9d80b31
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 30 deletions.
6 changes: 4 additions & 2 deletions playground/src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { join } from 'node:path'

import { app, shell, BrowserWindow } from 'electron'
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
import { registerIpcHandler } from 'electron-typed-ipc-bridge/main'
import { getIpcApiEmitter, registerIpcHandler } from 'electron-typed-ipc-bridge/main'

import icon from '../../resources/icon.png?asset'
import { api } from './api'
Expand Down Expand Up @@ -59,7 +59,9 @@ app.whenReady().then(() => {
})

// IPC test
const ipcApi = registerIpcHandler(api)
registerIpcHandler(api)

const ipcApi = getIpcApiEmitter(api)

createWindow(ipcApi)

Expand Down
12 changes: 4 additions & 8 deletions src/__tests__/main.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { API_CHANNEL_MAP } from '../channel'
import { registerIpcHandler } from '../main'
import { API_CHANNEL_MAP, getApiChannelMap } from '../channel'
import { getIpcApiEmitter, registerIpcHandler } from '../main'

import type { BrowserWindow, IpcMainInvokeEvent } from 'electron'

Expand Down Expand Up @@ -95,13 +95,9 @@ describe('main', () => {
send: mockSend,
},
} as unknown as BrowserWindow
const api = registerIpcHandler(_apiHandlers)
const api = getIpcApiEmitter(_apiHandlers)

const lastArgs = mocks.ipcMain.handle.mock.calls[0]
expect(lastArgs[0]).toBe(API_CHANNEL_MAP)

const ipcBridgeApiChannelGetter = lastArgs[1]
const channelMap = ipcBridgeApiChannelGetter()
const channelMap = getApiChannelMap(_apiHandlers)

api.send.fn2(browserWindow, 1, 2)
expect(mockSend).toBeCalled()
Expand Down
8 changes: 4 additions & 4 deletions src/__tests__/types.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { BrowserWindow, IpcMainInvokeEvent, IpcRendererEvent } from 'electron'
import type { RemoveEventArg, IpcBridgeApiTypeGenerator, IpcBridgeApiInvoker } from '../preload'
import type { ApiChannelMapGenerator } from '../channel'
import type { IpcBridgeApiSenderTypeGenerator } from '../main'
import type { IpcBridgeApiEmitterTypeGenerator } from '../main'

describe('Type check', () => {
it('RemoveEventArg', () => {
Expand Down Expand Up @@ -83,7 +83,7 @@ describe('Type check', () => {
},
}

type TestTarget = IpcBridgeApiSenderTypeGenerator<typeof _apiHandlers>
type TestTarget = IpcBridgeApiEmitterTypeGenerator<typeof _apiHandlers>
type ExpectedType = {
send: {
fn1: (window: BrowserWindow, arg1: string) => void
Expand All @@ -105,7 +105,7 @@ describe('Type check', () => {
},
}

type TestTarget = IpcBridgeApiSenderTypeGenerator<typeof _apiHandlers>
type TestTarget = IpcBridgeApiEmitterTypeGenerator<typeof _apiHandlers>

expectTypeOf<undefined>().toEqualTypeOf<TestTarget>()
})
Expand All @@ -122,7 +122,7 @@ describe('Type check', () => {
},
}

type TestTarget = IpcBridgeApiSenderTypeGenerator<typeof _apiHandlers>
type TestTarget = IpcBridgeApiEmitterTypeGenerator<typeof _apiHandlers>
type ExpectedType = {
send: {
fn1: (window: BrowserWindow, arg1: string) => void
Expand Down
30 changes: 29 additions & 1 deletion src/channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,37 @@ export type ApiChannelMapGenerator<T extends IpcBridgeApiImplementation> = {
invoke: ApiChannelMapItemTypeGenerator<T['invoke']>
}

let channelMap = undefined

function haveSameStructure(obj1, obj2) {
if (!obj1 || !obj2) {
return false
}
if (typeof obj1 !== 'object' && typeof obj2 !== 'object') {
return true
}
if (typeof obj1 !== 'object' || typeof obj2 !== 'object') {
return false
}

const keys1 = [...Object.keys(obj1)]
const keys2 = [...Object.keys(obj2)]

if (keys1.length !== keys2.length) {
return false
}

return keys1.every((key) => keys2.includes(key) && haveSameStructure(obj1[key], obj2[key]))
}

function getApiChannelMap<T extends IpcBridgeApiImplementation>(
apiHandlers: T
): ApiChannelMapGenerator<T>
function getApiChannelMap(apiHandlers: IpcBridgeApiImplementation) {
if (channelMap && haveSameStructure(apiHandlers, channelMap)) {
return channelMap
}

const _getApiChannelMap = (apiHandler: ApiHandler) => {
const channelMap: ApiChannelMapItem = {}
Object.keys(apiHandler).forEach((key) => {
Expand All @@ -57,7 +84,8 @@ function getApiChannelMap(apiHandlers: IpcBridgeApiImplementation) {
})
return channelMap
}
return _getApiChannelMap(apiHandlers)
channelMap = _getApiChannelMap(apiHandlers)
return channelMap
}

const MODE = {
Expand Down
33 changes: 23 additions & 10 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ type IpcBridgeApiSenderTypeConverter<T extends ApiOnHandler> = {
: never
}

export type IpcBridgeApiSenderTypeGenerator<T extends IpcBridgeApiImplementation> =
export type IpcBridgeApiEmitterTypeGenerator<T extends IpcBridgeApiImplementation> =
'on' extends keyof T
? T['on'] extends undefined
? undefined
Expand All @@ -38,13 +38,21 @@ export type IpcBridgeApiSenderTypeGenerator<T extends IpcBridgeApiImplementation
: never
: undefined

function registerIpcHandler<T extends IpcBridgeApiImplementation>(
ipcBridgeApi: T
): IpcBridgeApiSenderTypeGenerator<T>
function registerIpcHandler<T extends IpcBridgeApiImplementation>(ipcBridgeApi: T): void
function registerIpcHandler(ipcBridgeApi: IpcBridgeApiImplementation) {
createhandler(ipcBridgeApi, MODE.invoke)
}

function getIpcApiEmitter<T extends IpcBridgeApiImplementation>(
ipcBridgeApi: T
): IpcBridgeApiEmitterTypeGenerator<T>
function getIpcApiEmitter(ipcBridgeApi: IpcBridgeApiImplementation) {
return createhandler(ipcBridgeApi, MODE.on)
}
function createhandler(ipcBridgeApi: IpcBridgeApiImplementation, mode: ApiMode) {
const channelMap = getApiChannelMap(ipcBridgeApi)

let mode: ApiMode
let _mode: ApiMode
const _registerIpcHandler = (api: ApiHandler = ipcBridgeApi, apiInfo = channelMap, level = 0) => {
const keys = Object.keys(apiInfo)
if (level === 0) {
Expand All @@ -53,13 +61,16 @@ function registerIpcHandler(ipcBridgeApi: IpcBridgeApiImplementation) {
const sender = {}
keys.forEach((key) => {
if (level === 0) {
mode = MODE[key]
_mode = MODE[key]
if (MODE[key] !== mode) {
return
}
}
const senderKey = level === 0 ? 'send' : key

if (typeof apiInfo[key] === 'object' && !isApiFunction(api[key])) {
console.debug(`${' '.repeat(level)} - ${key}`)
switch (mode) {
switch (_mode) {
case MODE.invoke:
_registerIpcHandler(api[key], apiInfo[key], level + 1)
break
Expand All @@ -71,7 +82,7 @@ function registerIpcHandler(ipcBridgeApi: IpcBridgeApiImplementation) {
}
} else if (typeof apiInfo[key] !== 'object' && isApiFunction(api[key])) {
console.debug(`${' '.repeat(level)} - ${key} (chanel: ${apiInfo[key]})`)
switch (mode) {
switch (_mode) {
case MODE.invoke:
ipcMain.handle(apiInfo[key], api[key])
break
Expand All @@ -95,8 +106,10 @@ function registerIpcHandler(ipcBridgeApi: IpcBridgeApiImplementation) {
}
return sender
}
ipcMain.handle(API_CHANNEL_MAP, () => channelMap)
if (mode === MODE.invoke) {
ipcMain.handle(API_CHANNEL_MAP, () => channelMap)
}
return _registerIpcHandler()
}

export { registerIpcHandler }
export { registerIpcHandler, getIpcApiEmitter }
11 changes: 6 additions & 5 deletions src/types/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { IpcContextBridgeApi } from '../preload'
import type { IpcBridgeApiSenderTypeGenerator } from '../main'
import type { IpcBridgeApiEmitterTypeGenerator } from '../main'
import type { IpcBridgeApiImplementation } from '../channel'

/**
Expand All @@ -12,18 +12,19 @@ export type { IpcBridgeApiTypeGenerator } from '../preload'
* Type generator for api to send message from main to renderer
* Use at the main process
*/
export type { IpcBridgeApiSenderTypeGenerator } from '../main'
export type { IpcBridgeApiEmitterTypeGenerator as IpcBridgeApiSenderTypeGenerator } from '../main'

/**
* Resister IPC handler(for tow way)
* and generate api for send message to renderer process
* Use at the main process
* @param ipcBridgeApi Implementation for IPC api handlers
*/
export function registerIpcHandler<T extends IpcBridgeApiImplementation>(
ipcBridgeApi: T
): IpcBridgeApiSenderTypeGenerator<T>
export function registerIpcHandler<T extends IpcBridgeApiImplementation>(ipcBridgeApi: T): void

export function getIpcApiEmitter<T extends IpcBridgeApiImplementation>(
ipcBridgeApi: T
): IpcBridgeApiEmitterTypeGenerator<T>
/**
* Generate IPC api that will be exposed to renderer process.
* Use at the preload sctipt
Expand Down

0 comments on commit 9d80b31

Please sign in to comment.