Files
AFFiNE-Mirror/packages/frontend/apps/electron/scripts/ipc-generator/utils.ts
2025-06-18 15:34:09 +08:00

112 lines
4.1 KiB
TypeScript

import {
type CollectedApisMap,
type CollectedEventsMap,
type Entry,
} from './types';
export const determineEntry = (path: string): Entry => {
if (path.includes('src/entries/helper')) return 'helper';
return 'main';
};
/**
* Generates the API type definitions file content
*/
export function generateApiTypesFile(collectedApis: CollectedApisMap): string {
let content = `// AUTO-GENERATED FILE. DO NOT EDIT MANUALLY.\n// Generated by: packages/frontend/apps/electron/scripts/generate-types.ts\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\n`;
content += `// Utility type: remove trailing IpcMainInvokeEvent param and ensure Promise return\n`;
content += `type EnsurePromise<T> = T extends Promise<any> ? T : Promise<T>;\n`;
content += `export type ApiMethod<T> = T extends (...args: infer P) => infer R ? (...args: P) => EnsurePromise<R> : never;\n\n`;
content += `export interface ElectronApis {\n`;
for (const [scope, methods] of collectedApis) {
content += ` ${scope}: {\n`;
for (const method of methods) {
if (method.description && method.description.trim().length > 0) {
const lines = method.description.split('\n').map(l => ` * ${l}`);
content += ' /**\n';
content += lines.join('\n') + '\n';
content += ' */\n';
}
content += ` ${method.apiMethodName}: ApiMethod<import('${method.modulePath}').${method.className}['${method.methodName}']>;\n`;
}
content += ` };\n`;
}
content += `}\n`;
return content;
}
/**
* Generates the event type definitions file content
*/
export function generateEventTypesFile(
collectedEvents: CollectedEventsMap
): string {
let out = `// AUTO-GENERATED FILE. DO NOT EDIT MANUALLY.\n// Generated by: packages/frontend/apps/electron/scripts/generate-types.ts\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport type { Observable } from 'rxjs';\n\n`;
out += `type ToSubscribe<T extends Observable<unknown>> = T extends Observable<infer P> ? (callback: (payload: P) => void) => () => void : never;\n\n`;
out += `export interface ElectronEvents {\n`;
for (const [scope, events] of collectedEvents) {
out += ` ${scope}: {\n`;
for (const evt of events) {
const capName =
evt.eventName.charAt(0).toUpperCase() + evt.eventName.slice(1);
if (evt.description && evt.description.trim().length > 0) {
const lines = evt.description.split('\n').map(l => ` * ${l}`);
out += ' /**\n';
out += lines.join('\n') + '\n';
out += ' */\n';
}
out += ` on${capName}: ToSubscribe<import('${evt.modulePath}').${evt.className}['${evt.propertyName}']>;\n`;
}
out += ` };\n`;
}
out += `}\n`;
return out;
}
/**
* Generates a combined metadata file for both IPC handlers and events
*/
export function generateCombinedMetaFile(
apiHandlers: CollectedApisMap,
events: CollectedEventsMap
): string {
let content = `// AUTO-GENERATED FILE. DO NOT EDIT MANUALLY.\n// Generated by: packages/frontend/apps/electron/scripts/generate-types.ts\n/* eslint-disable @typescript-eslint/no-explicit-any */\n`;
// Build nested structure: entry -> scope -> names[]
const handlersMeta: Record<string, Record<string, string[]>> = {};
apiHandlers.forEach((methods, scope) => {
methods.forEach(m => {
const ent = m.entry;
if (!handlersMeta[ent]) handlersMeta[ent] = {};
if (!handlersMeta[ent][scope]) handlersMeta[ent][scope] = [];
handlersMeta[ent][scope].push(m.apiMethodName);
});
});
const eventsMeta: Record<string, Record<string, string[]>> = {};
events.forEach((evtList, scope) => {
evtList.forEach(e => {
const ent = e.entry;
if (!eventsMeta[ent]) eventsMeta[ent] = {};
if (!eventsMeta[ent][scope]) eventsMeta[ent][scope] = [];
eventsMeta[ent][scope].push(e.eventName);
});
});
// Serialize handlersMeta & eventsMeta
content += `export const handlersMeta = ${JSON.stringify(
handlersMeta,
null,
2
)} as const;\n\n`;
content += `export const eventsMeta = ${JSON.stringify(eventsMeta, null, 2)} as const;\n`;
return content;
}