HaNdTriX / next-electron-server

Server your Next.js app inside electron using a custom scheme.
https://npmjs.com/package/next-electron-server
27 stars 6 forks source link

Support Next.js Version 12 #7

Closed HaNdTriX closed 1 year ago

HaNdTriX commented 2 years ago

Next.js switched from using the EventSource API for development tooling to the WebSockets API.

Fast Refresh now uses a WebSocket connection instead of a EventSource connection.

Source: https://nextjs.org/blog/next-12#other-improvements

This breaks the next-electron-server due to a protocol mismatch.

Quick Hacks:

niteshbalusu11 commented 2 years ago

Maybe related?

https://github.com/HaNdTriX/next-electron-server/issues/8

HaNdTriX commented 2 years ago

Yes it is related. Currently I don't have a perfect solution to this problem, jet.

But you can add the following line to your preload script to patch the invalid WebSocket->url of the Next.js DevServer:

Requires next-electron-server Version: 0.2.0

const { webFrame } = require("electron");

// Next.js Websocket DevServer is not listining on our custom scheme.
// This is why we need to monkey patch the global WebSocket constructor
// to use the correct DevServer url
// More info: https://github.com/HaNdTriX/next-electron-server/issues/7
if (process.env.NEXT_ELECTON_SERVER_DEV === "true") {
  webFrame.executeJavaScript(`Object.defineProperty(globalThis, 'WebSocket', {
    value: new Proxy(WebSocket, {
      construct: (Target, [url, protocols]) => {
        if (url.endsWith('/_next/webpack-hmr')) {
          // Fix the Next.js hmr client url
          return new Target("ws://localhost:${
            process.env.NEXT_ELECTON_SERVER_PORT || 3000
          }/_next/webpack-hmr", protocols)
        } else {
          return new Target(url, protocols)
        }
      }
    })
  });`);
}

Check out the following example to see this patch in action. In the future this logic should be implemented by this package. This allows us to be port agnostic.

niteshbalusu11 commented 2 years ago

It worked, thank you!

andirsun commented 2 years ago

@niteshbalusu11 Do you have an example working with nextron ?

niteshbalusu11 commented 2 years ago

@niteshbalusu11 Do you have an example working with nextron ?

Modified background.js

import serveNextAt from 'next-electron-server';

const isProd: boolean = process.env.NODE_ENV === 'production';

serveNextAt('next://app', {
  outputDir: './app',
  port: 8888,
});

(async () => {
  await app.whenReady();

  const mainWindow = createWindow('main', {
    width: 1000,
    height: 600,
  });

  if (!!isProd) {
    await mainWindow.loadURL('next://app/home');
  } else {
    await mainWindow.loadURL('next://app/home');
    mainWindow.webContents.openDevTools();
  }
})();

And if you're using Nextjs 12 (which I guess is the default for latest version of Nextron), here's what I had to add to preload.js

import { webFrame } from 'electron';

// Next.js Websocket DevServer is not listining on our custom scheme.
// This is why we need to monkey patch the global WebSocket constructor
// to use the correct DevServer url
// More info: https://github.com/HaNdTriX/next-electron-server/issues/7
if (process.env.NEXT_ELECTON_SERVER_DEV === "true") {
  webFrame.executeJavaScript(`Object.defineProperty(globalThis, 'WebSocket', {
    value: new Proxy(WebSocket, {
      construct: (Target, [url, protocols]) => {
        if (url.endsWith('/_next/webpack-hmr')) {
          // Fix the Next.js hmr client url
          return new Target("ws://localhost:${
            process.env.NEXT_ELECTON_SERVER_PORT || 3000
          }/_next/webpack-hmr", protocols)
        } else {
          return new Target(url, protocols)
        }
      }
    })
  });`);
}
jamesvibar commented 2 years ago

Hi @HaNdTriX , I've managed to make it work with nextron and thank you so much for your work!

But whenever I try to create a BrowserWindow with session (partition) I get a served a blank page and I see this error on my console:

next-electron-server - Serving files via next://app from http://localhost:8888
[9371:0810/010110.128562:ERROR:bus.cc(397)] Failed to connect to the bus: Could not parse server address: Unknown address type (examples of valid types are "tcp" and on UNIX "unix")
(node:9371) UnhandledPromiseRejectionWarning: Error: ERR_FAILED (-2) loading 'next://app/home'
    at rejectAndCleanup (node:electron/js2c/browser_init:161:7647)
    at EventEmitter.stopLoadingListener (node:electron/js2c/browser_init:161:8022)
    at EventEmitter.emit (node:events:390:28)
(Use `electron --trace-warnings ...` to show where the warning was created)
(node:9371) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
libGL error: No matching fbConfigs or visuals found
libGL error: failed to load driver: swrast
LaunchProcess: failed to execvp:
xdg-open

My code:

  const _session = session.fromPartition('test')
  const mainWindow = createWindow('main', {
    width: 1000,
    height: 600,
    show: false,
    webPreferences: {
      webviewTag: true,
      session: _session,
    },
  })

createWindow is the default helper function included by default in nextron

HaNdTriX commented 2 years ago

@jamesvibar thanks for raising this issue. I have just released a new version (0.1.0) that adds support for different partitions.

Please update next-electron-server to the latest version and check out the following code:

const { join } = require("path");
const { BrowserWindow, app, ipcMain } = require("electron");
const serveNext = require("next-electron-server");

// Register your own scheme and host
- serveNext("next://app");
+ serveNext("next://app", {
+   partition: "persist:somepartition",
+ });

app.whenReady().then(async () => {
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: false,
+      partition: "persist:somepartition",
      preload: join(__dirname, "preload.js"),
    },
  });

  // Load renderer using a custom protocol:
  mainWindow.loadURL("next://app");

Commit: https://github.com/HaNdTriX/next-electron-server/commit/44af68e63a507f5486b900329f2751cc3969ab2f

Hope this was helpful.


Please note, that I will hide our comments in this issue, tomorrow, since they are not related to Next.js Version 12