karolis-sh / electron-snowpack

Use Snowpack and esbuild for Electron app development
MIT License
50 stars 8 forks source link

Using Preload scripts with typescript + react #62

Closed aw1875 closed 2 years ago

aw1875 commented 2 years ago

I've been trying to move all my electron projects over from webpack to snowpack recently as I've loved it so much for web applications. I'm having a few issues trying to add a preload script so if I could get some help/verification that would be great!

Within my index.ts (which is located inside src > main) I currently have this code while creating my browser window:

webPreferences: {
      devTools: !app.isPackaged,
      nodeIntegration: false,
      contextIsolation: true,
      preload: path.join(__dirname, 'preload.ts'),
},

I then wrote my preload functions and declared my types in my preload.d.ts file but I haven't had any luck calling my functions from my renderer code. I have all my appropriate event listeners setup in my index.ts file as well. I'd appreciate any help.

Thanks!

aw1875 commented 2 years ago

I should probably note that vscode makes the connection to my function within the preload script but when I try to trigger the function I get an error such as this:

Uncaught TypeError: Cannot read property 'close' of undefined
aw1875 commented 2 years ago

Alright I'm just stupid. I've now realized that my preload script isn't being built so when the renderer looks for it, it turns out to be undefined. Any idea why it isn't being compiled? I have a few other files within the main folder that I would need compiled and it looks like none of them are compiling.

karolis-sh commented 2 years ago

I didn't make an explicit example with preload, but there's one in the playground project:

It might be related to context isolation and the way you've implemented the preload script (https://www.electronjs.org/docs/latest/tutorial/context-isolation). I haven't used preload in practice that much, but that might be the reason according to the docs. Anyway, a mini reproduction project would be nice, as otherwise it's a bit hard to identify the issue.

aw1875 commented 2 years ago

Thanks for the response. It seems the main issue is my preload.ts file is not being compiled as it doesn't show up in the dist folder after starting the project with snowpack dev. I'm not sure if I have to make more modifications to my snowpack.config.mjs file for this to happen but I also have some object's that I will be commonly using throughout the project that I need to be compiled from my main folder as well. I'd be more than happy to share a mini reproduction or any code necessary to get this solved so just let me know! Again, apologize for the questions I'm just super new to using snowpack with electron and I feel like combining it with react and typescript may not be the best first approach for a beginner here.

aw1875 commented 2 years ago

I'd also like to share a previous example of some code for my projects when I used webpack. My understanding is using context isolation paired with a context bridge of some sort through the preload script is the approach electron expects you to take now as it is the safest route (instead of exposing functions like os and fs to the renderer). Anyways, here is a code sample below from the webpack version:

index.ts

declare const MAIN_WINDOW_WEBPACK_ENTRY: string;
declare const MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY: string;

export const assetsPath =
    process.env.NODE_ENV === "production"
        ? process.resourcesPath
        : app.getAppPath();

const createWindow = () => {
    mainWindow = new BrowserWindow({
        icon: path.join(assetsPath, "assets", "icon.ico"),
        width: 1260,
        height: 650,
        minWidth: 1260,
        minHeight: 650,
        transparent: true,
        opacity: 0.97,
        frame: false,
        autoHideMenuBar: true,
        webPreferences: {
            devTools: !app.isPackaged,
            nodeIntegration: false,
            contextIsolation: true,
            preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY,
        },
    });

    mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY);

    mainWindow.on("closed", () => {
        mainWindow = null;
    });
};

preload.ts

import { contextBridge, ipcRenderer } from "electron";

export const api = {
    send: (channel: string, data: any) => {
        let validChannels = [
            "checkUpdates",
            "close",
            "maximize",
            "minimize",
        ];
        if (validChannels.includes(channel)) {
            ipcRenderer.send(channel, data);
        }
    },

    invoke: async (channel: string, data: any) => {
        let validInvokeChannels = [
            "getData",
        ];
        if (validInvokeChannels.includes(channel)) {
            return await ipcRenderer.invoke(channel, data);
        }
    },

    on: (channel: string, callback: Function) => {
        ipcRenderer.on(channel, (_, data) => callback(data));
    },
};

contextBridge.exposeInMainWorld("Main", api);

Example using the context bridge within my renderer process

window.Main.on("updateProgress", (progress: string) => {
    setUpdating(progress);
});
aw1875 commented 2 years ago

It looks like the issue was just naming. I had previously named my preload file bridge.ts in the project I was trying to move over so once I renamed it preload.ts it seemed to compile properly. Everything works now thanks for the help!