automate-electron-ipc
Version:
Node library for automating the generation of IPC components for Electron apps.
108 lines (107 loc) • 5.09 kB
JavaScript
/*
* Apache License 2.0
*
* Copyright (c) 2024, Mattias Aabmets
*
* The contents of this file are subject to the terms and conditions defined in the License.
* You may not use, modify, or distribute this file except in compliance with the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
import { BaseWriter } from "./base-writer.js";
export class MainBindingsWriter extends BaseWriter {
getTargetFilePath() {
return this.config.mainBindingsFilePath;
}
renderEmptyFileContents() {
return "\nexport const ipcMain = {};";
}
renderFileContents() {
const electronImportsSet = new Set(["ipcMain as electronIpcMain"]);
const electronTypeImportsSet = new Set(["IpcMainEvent"]);
const importDeclarationsArray = [];
const callablesArray = [];
const portsArray = [];
for (const parsedFileSpecs of this.pfsArray) {
let customTypes = new Set();
for (const spec of parsedFileSpecs.specs.channelSpecArray) {
if (spec.direction === "RendererToMain") {
this.addRendererToMainCallables(spec, callablesArray);
}
else if (spec.direction === "MainToRenderer") {
electronTypeImportsSet.add("BrowserWindow");
callablesArray.push(this.buildMainToRendererCallable(spec));
}
else if (spec.direction === "RendererToRenderer") {
electronImportsSet.add("MessageChannelMain");
electronTypeImportsSet.add("BrowserWindow");
portsArray.push(this.buildRendererToRendererPort(spec));
}
const specCustomTypes = new Set(spec.signature.customTypes);
customTypes = customTypes.union(specCustomTypes);
}
for (const customType of customTypes) {
const importDeclaration = this.importsGenerator.getDeclaration(parsedFileSpecs, customType);
if (importDeclaration) {
importDeclarationsArray.push(importDeclaration);
}
}
}
const out = [
`import { ${Array.from(electronImportsSet).join(", ")} } from "electron";`,
`import type { ${Array.from(electronTypeImportsSet).join(", ")} } from "electron";`,
...importDeclarationsArray,
];
const sortedCallablesArray = this.sortCallablesArray(callablesArray);
const sortedCallables = sortedCallablesArray.join(`,\n${this.indents[0]}`);
const bindingsExpression = [
"\nexport const ipcMain = {",
`\n${this.indents[0]}${sortedCallables},`,
];
if (portsArray.length > 0) {
bindingsExpression.push(...[`\n${this.indents[0]}ports: {`, ...portsArray, `\n${this.indents[0]}},`]);
}
bindingsExpression.push("\n}\n");
out.push(bindingsExpression.join(""));
return out.join("\n");
}
addRendererToMainCallables(spec, callablesArray) {
const method = spec.kind === "Broadcast" ? "on" : "handle";
const callback = "(event: any, ...args: any[]) => (callback as any)(event, ...args)";
const ipcMain = `\n${this.indents[1]}electronIpcMain.${method}('${spec.name}', ${callback})`;
const modSigDef = this.injectEventTypehint(spec.signature.definition);
const callableNames = spec.listeners ? spec.listeners : [`on${spec.name}`];
callableNames.forEach((name) => {
callablesArray.push(`${name}: (callback: ${modSigDef}) => ${ipcMain}`);
});
}
buildMainToRendererCallable(spec) {
const senderParams = this.getOriginalParams(spec, true);
let sender = `browserWindow.webContents.send('${spec.name}', ${senderParams})`;
if (spec.trigger) {
sender = `browserWindow.on("${spec.trigger}", () => ${sender})`;
}
const ipcParams = this.getOriginalParams(spec, false);
const ipcSignature = `(browserWindow: BrowserWindow, ${ipcParams})`;
return `send${spec.name}: ${ipcSignature} => \n${this.indents[1]}${sender}`;
}
buildRendererToRendererPort(spec) {
const ipcSig = "(bwOne: BrowserWindow, bwTwo: BrowserWindow)";
const propagator = [
"{",
`${this.indents[3]}const { port1, port2 } = new MessageChannelMain();`,
`${this.indents[3]}bwOne.once('ready-to-show', () => {`,
`${this.indents[4]}bwOne.webContents.postMessage('${spec.name}', null, [port1]);`,
`${this.indents[3]}});`,
`${this.indents[3]}bwTwo.once('ready-to-show', () => {`,
`${this.indents[4]}bwTwo.webContents.postMessage('${spec.name}', null, [port2]);`,
`${this.indents[3]}});`,
`${this.indents[2]}},`,
].join("\n");
return [
`\n${this.indents[1]}${spec.name}: {`,
`\n${this.indents[2]}propagate: ${ipcSig} => ${propagator}`,
`\n${this.indents[1]}},`,
].join("");
}
}