Open syabro opened 1 year ago
Upd:
found https://vitejs.dev/guide/api-plugin.html#universal-hooks + closeBuild
in https://github.com/vitejs/vite/discussions/9217
// vite.config.ts
export default defineConfig({
build: {
watch: {
include: 'src/**'
},
} ...
plugins: [
{
name: 'postbuild-commands', // the name of your custom plugin. Could be anything.
closeBundle: async () => {
await postBuildCommands() // run during closeBundle hook. https://rollupjs.org/guide/en/#closebundle
}
},
]
})
I guess we can write a tiny plugin that would open a process with tsc
on on build start and close on build end...
UPD2: Used writeBundle
hook, works :)
// electron.vite.config.ts
import { exec } from 'child_process'
// ...
export default defineConfig({
// ...
preload: {
plugins: [
// ...
{
name: 'rebuild-api-types',
writeBundle: (options: any, bundle: { [fileName: string]: any }) => {
exec(
'tsc --declaration --emitDeclarationOnly --outDir ./src/preload/ ./src/preload/api.ts',
(error, stdout, stderr) => {
if (error) return console.error(`Error: ${error.message}`)
if (stderr) return console.error(`Stderr: ${stderr}`)
console.log(' regenerated api.d.ts')
}
)
}
}
],
// ...
},
// ...
})
// preload/index.d.ts
import { ElectronAPI } from '@electron-toolkit/preload'
declare global {
interface Window {
electron: ElectronAPI
api: typeof preloadApi
}
}
duplicate #121
@alex8088 I've upgraded my approach - it generates api from handlers automatically so less boilerplate
// src/preload/api.ts
import { ipcRenderer } from 'electron'
import { ipcHandlers } from '../src-main/handlers'
type ApiFromHandlers<T extends Record<string, (event: unknown, ...args: any[]) => any>> = {
[K in keyof T]: (
...args: Parameters<T[K]> extends [unknown, ...infer R] ? R : never
) => Promise<ReturnType<T[K]>>
}
function createApi<T extends Record<string, (event: unknown, ...args: any[]) => any>>(
handlers: T
): ApiFromHandlers<T> {
const api: Partial<ApiFromHandlers<T>> = {}
for (const key in handlers) {
api[key as keyof T] = (...args: any[]) => ipcRenderer.invoke(key as string, ...args)
}
return api as ApiFromHandlers<T>
}
export const api = createApi(ipcHandlers)
declare global {
type Api = typeof api
}
// src/main/ipcHandlers
export const ipcHandlers = {
downloadPreset: async (event: any, params: { packName: string, url: string, name: string, vst: string }) => {
},
checkPresetInstalled: (event, params: { packName: string, name: string, vst: string }): boolean | Error => {
},
}
export type IpcHandlers = typeof ipcHandlers
@syabro Thank you! I now have types for my preload api ⚡
@alex8088 Thank you for this great electron framework 😄
For automation, I added a typecheck:preload
script to my package.json
and called it from the existing typecheck
script:
{
"scripts": {
"typecheck:preload": "tsc --declaration --emitDeclarationOnly --outDir ./src/preload/ ./src/preload/api.ts",
"typecheck": "yarn typecheck:preload && yarn typecheck:node && yarn typecheck:web",
...
}
}
Now if I make changes, I can just build the app and the typings will be regenerated for me.
I think we may have a better solution to this problem. For now, we are considering unplugin-auto-expose or unplugin-elexpse as a possible solution. But they have some limitations and don't work out of the box.
Considerations for out-of-the-box:
d.ts
file
Clear and concise description of the problem
Currently api type is unknown
And I have to replace it with real api and keep them synced
Suggested solution
api.ts
import { api } from "./api"; // 👈🏻 new import
if (process.contextIsolated) { try { contextBridge.exposeInMainWorld('electron', electronAPI) contextBridge.exposeInMainWorld('api', api) } catch (error) { console.error(error) } } else { // @ts-ignore (define in dts) window.electron = electronAPI // @ts-ignore (define in dts) window.api = api }
Generate api types
Have fun
Alternative
No response
Additional context
The problem here I don't know how to call api type compilation somewhere from vite without running
tsc
separately :(Validations