jsonnull/electron-trpc

`process is not defined` after importing ipcLink in renderer

Closed this issue · 10 comments

At first, that entire code works if I copy/paste it into my app. When I tried to use the npm package I faced with that issue:

CleanShot 2022-11-18 at 12 43 40@2x

I am using electron-react-boilerplate

I think it is caused because I am using import { ipcLink } from 'electron-trpc';. When I am trying to use import { ipcLink } from 'electron-trpc/renderer'; I am getting:

Module not found: Error: Package path ./renderer is not exported from package [...]/node_modules/electron-trpc

Hi, sorry for getting back to you late.

Can you clarify for me which versions of trpc and electron-trpc you are using?

If you were running this off the next branch, you needed to be on electron-trpc@next and trpc v10 pre-release.

Today I've cut the 0.2.0 release of electron-trpc which works with trpc v10. If you're in a position to update to these versions and can compare with the example in the repo, if you're still having issues I'll be able to help further.

I installed a new version 0.2.0 and I faced an issue with Electron React Boilerplate webpack's build step which is run always after adding a new package:

[4/4] 🔨  Building fresh packages...
success Saved lockfile.
success Saved 1 new dependency.
info Direct dependencies
└─ electron-trpc@0.2.0
info All dependencies
└─ electron-trpc@0.2.0
$ ts-node .erb/scripts/check-native-dep.js && electron-builder install-app-deps && cross-env NODE_ENV=development TS_NODE_TRANSPILE_ONLY=true webpack --config ./.erb/configs/webpack.config.renderer.dev.dll.ts
{"type":"warning","data":"Filtering by arguments is deprecated. Please use the pattern option instead."}
Native dependencies could not be checked
  • electron-builder  version=23.6.0
  • loaded configuration  file=[...]/electron-builder.json
  • installing production dependencies  platform=darwin arch=arm64 appDir=[...]/release/app
ERROR in dll renderer renderer[20]
Module not found: Error: Package path . is not exported from package [...]/node_modules/electron-trpc (see exports field in [...]/node_modules/electron-trpc/package.json)

webpack compiled with 1 error
error Command failed with exit code 1.

If I add electron-trpc to webpack's externals in webpack.config.renderer.dev.dll.ts then it works, but after that I am getting another issue while importing createIPCHandler: Cannot find module 'electron-trpc/main' or its corresponding type declarations.

I added electron-trpc@0.2.0 to a new electron-react-boilerplate project and I didn't get the error you mentioned when installing.

I'm also able to get a working query using just the example in the project here. I'm adding the diff below, if you want to see what that looks like.

If you're still having issues, maybe you can push your project to a repo I can pull down to reproduce?

diff --git a/src/main/main.ts b/src/main/main.ts
index 327b81f..d3137c4 100644
--- a/src/main/main.ts
+++ b/src/main/main.ts
@@ -14,6 +14,8 @@ import { autoUpdater } from 'electron-updater';
 import log from 'electron-log';
 import MenuBuilder from './menu';
 import { resolveHtmlPath } from './util';
+import { createIPCHandler } from 'electron-trpc/main';
+import { router } from './router';
 
 class AppUpdater {
   constructor() {
@@ -25,12 +27,6 @@ class AppUpdater {
 
 let mainWindow: BrowserWindow | null = null;
 
-ipcMain.on('ipc-example', async (event, arg) => {
-  const msgTemplate = (pingPong: string) => `IPC test: ${pingPong}`;
-  console.log(msgTemplate(arg));
-  event.reply('ipc-example', msgTemplate('pong'));
-});
-
 if (process.env.NODE_ENV === 'production') {
   const sourceMapSupport = require('source-map-support');
   sourceMapSupport.install();
@@ -128,6 +124,7 @@ app
   .whenReady()
   .then(() => {
     createWindow();
+    createIPCHandler({ router });
     app.on('activate', () => {
       // On macOS it's common to re-create a window in the app when the
       // dock icon is clicked and there are no other windows open.
diff --git a/src/main/preload.ts b/src/main/preload.ts
index d35fdfe..5e4212a 100644
--- a/src/main/preload.ts
+++ b/src/main/preload.ts
@@ -1,23 +1,5 @@
-import { contextBridge, ipcRenderer, IpcRendererEvent } from 'electron';
+import { exposeElectronTRPC } from 'electron-trpc/main';
 
-export type Channels = 'ipc-example';
-
-contextBridge.exposeInMainWorld('electron', {
-  ipcRenderer: {
-    sendMessage(channel: Channels, args: unknown[]) {
-      ipcRenderer.send(channel, args);
-    },
-    on(channel: Channels, func: (...args: unknown[]) => void) {
-      const subscription = (_event: IpcRendererEvent, ...args: unknown[]) =>
-        func(...args);
-      ipcRenderer.on(channel, subscription);
-
-      return () => {
-        ipcRenderer.removeListener(channel, subscription);
-      };
-    },
-    once(channel: Channels, func: (...args: unknown[]) => void) {
-      ipcRenderer.once(channel, (_event, ...args) => func(...args));
-    },
-  },
+process.once('loaded', () => {
+  exposeElectronTRPC();
 });
diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx
index eb321c5..a58e09a 100644
--- a/src/renderer/App.tsx
+++ b/src/renderer/App.tsx
@@ -1,14 +1,24 @@
+import { useState } from 'react';
 import { MemoryRouter as Router, Routes, Route } from 'react-router-dom';
+import { createTRPCReact } from '@trpc/react-query';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { ipcLink } from 'electron-trpc/renderer';
+import type { AppRouter } from '../main/router';
 import icon from '../../assets/icon.svg';
 import './App.css';
 
+const trpcReact = createTRPCReact<AppRouter>();
+
 const Hello = () => {
+  const { data } = trpcReact.greeting.useQuery();
+
   return (
     <div>
       <div className="Hello">
         <img width="200" alt="icon" src={icon} />
       </div>
       <h1>electron-react-boilerplate</h1>
+      <h2>From backend: {data?.text}</h2>
       <div className="Hello">
         <a
           href="https://electron-react-boilerplate.js.org/"
@@ -40,11 +50,21 @@ const Hello = () => {
 };
 
 export default function App() {
+  const [queryClient] = useState(() => new QueryClient());
+
+  const [trpcClient] = useState(() =>
+    trpcReact.createClient({ links: [ipcLink()] })
+  );
+
   return (
-    <Router>
-      <Routes>
-        <Route path="/" element={<Hello />} />
-      </Routes>
-    </Router>
+    <trpcReact.Provider client={trpcClient} queryClient={queryClient}>
+      <QueryClientProvider client={queryClient}>
+        <Router>
+          <Routes>
+            <Route path="/" element={<Hello />} />
+          </Routes>
+        </Router>
+      </QueryClientProvider>
+    </trpcReact.Provider>
   );
 }
diff --git a/src/renderer/index.tsx b/src/renderer/index.tsx
index a96350e..b7b48c1 100644
--- a/src/renderer/index.tsx
+++ b/src/renderer/index.tsx
@@ -4,10 +4,3 @@ import App from './App';
 const container = document.getElementById('root')!;
 const root = createRoot(container);
 root.render(<App />);
-
-// calling IPC exposed from preload script
-window.electron.ipcRenderer.once('ipc-example', (arg) => {
-  // eslint-disable-next-line no-console
-  console.log(arg);
-});
-window.electron.ipcRenderer.sendMessage('ipc-example', ['ping']);

I also tried it on fresh ERB repo and both errors still occur.

I added electron-trpc@0.2.0 to a new electron-react-boilerplate project and I didn't get the error you mentioned when installing.

Could you try to run yarn postinstall or npm run postinstall? It occurs during this step.

I'm also able to get a working query using just the example in the project here. I'm adding the diff below, if you want to see what that looks like.

I hope it will work but IDE shows it can't find type declarations. I'm guessing it happens because of the lack of .d.ts in the package. Correct me if I am wrong.

CleanShot 2022-12-07 at 21 18 52@2x

I see, so there's two separate issues here.

One is in how that project's webpack.config.renderer.dev.dll.ts reads dependencies kinda naively. It's going to add electron-trpc to the entries, but that's not a valid entry point—this package has two possible entries, electron-trpc/main and electron-trpc/renderer. Here's a short fix for that:

diff --git a/.erb/configs/webpack.config.renderer.dev.dll.ts b/.erb/configs/webpack.config.renderer.dev.dll.ts
index 614b90f..fd1bdff 100644
--- a/.erb/configs/webpack.config.renderer.dev.dll.ts
+++ b/.erb/configs/webpack.config.renderer.dev.dll.ts
@@ -31,7 +31,9 @@ const configuration: webpack.Configuration = {
   module: require('./webpack.config.renderer.dev').default.module,
 
   entry: {
-    renderer: Object.keys(dependencies || {}),
+    renderer: Object.keys(dependencies || {}).map((dep) =>
+      dep === 'electron-trpc' ? 'electron-trpc/renderer' : dep
+    ),
   },
 
   output: {

The other issue is a mistake on my part, the .d.ts files were being generated but not included in the package. I've published electron-next@0.2.1 which fixes this.

Your setup may need to be tweaked to take advantage of this, however. When using the updated package in my ERB setup I had to update moduleResolution in the tsconfig.json from node to node16 for TS to take advantage. At that point I no longer got TS warnings about the types not being found, but I was still getting eslint warnings. You may want to see if there's an eslint setting or update needed to take advantage of the package.json exports field. EDIT: Looks like exports map is not supported by eslint-plugin-import yet: import-js/eslint-plugin-import#2580

Thank you for your help, now it works. Also, I don't have any issues with eslint-plugin-import neither in my project nor the ERB repo. Which eslint's warning do you get?

I have a little problem with automatic imports in VSC after changing moduleResolution to node16.
It imports automatically [...]/Example/Example or [...]/Example/index, but previously it was just [...]/Example. Of course, I can import it like that by hand but it is a little bit annoying when you need to do it manually.

CleanShot 2022-12-08 at 11 52 14@2x

Do you have any idea how to handle it? I never used node16 before.

Edit: It can also be a little bit annoying for other users of this package to do all of those setups so here are a few ideas:

  1. What do you think about getting rid of the exports field and splitting the electron-trpc package into two npm packages (electron-trpc/main and electron-trpc/renderer) with separated package.json files, but both placed in the same monorepo? Then setting moduleResolution to node16 will be unnecessary and also changing an ERB's webpack configuration will not be needed.
  2. On the other hand, maybe setting package.json's browser to renderer.js and main to main.js instead of exports will do the trick?
  3. Or just set package.json's types top field to point one index.d.ts instead of two separated in exports.

Do you have any idea how to handle it? I never used node16 before.

Another solution to stick with node instead of setting node16 is to add paths to tsconfig.json like:

"paths": {
  "electron-trpc/main": ["../node_modules/electron-trpc/dist/main"],
  "electron-trpc/renderer": ["../node_modules/electron-trpc/dist/renderer"],
}

Note: paths is relative to the baseUrl

But still, it needs to do some additional setup which feels like a workaround.

  • eslint-plugin-import gives an error for me

    Unable to resolve path to module 'electron-trpc/main'. [Error/import/no-unresolved]
    

    I think the config can be adjusted to not give the error for this package.

  • What do you think about getting rid of the exports field and splitting the electron-trpc package into two npm packages

    I'll think about this, but I'm inclined to keep things the way they are.

    Usage of the exports field is not so widespread yet, but I do expect it to become a more popular alternative to multiple packages in some cases. It's normal for there to be some hiccups as more projects are able to support this. For now, plenty of tooling supports this out-of-the-box (node, webpack, rollup, vite). TypeScript has supported it for a couple major versions. Requiring two packages to be installed is a guaranteed cost, having to reconfigure a project to support exports is a possible cost, but not every user will have to do this.

    electron-react-boilerplate could read traverse dependencies and read exports map itself when building its DLL config if it so chose (this would work for electron-trpc but not all projects), or take an approach other than the DLL.

I read more about exports and the node16 option. I also thought more about this case and I agree with you. I think we can close this as resolved. Thank you for your time.